ขนาดของสมาชิกโครงสร้างเดียวใน C


109

ฉันกำลังพยายามประกาศโครงสร้างที่ขึ้นอยู่กับโครงสร้างอื่น ฉันต้องการใช้sizeofเพื่อความปลอดภัย / อวดรู้

typedef struct _parent
{
  float calc ;
  char text[255] ;
  int used ;
} parent_t ;

ตอนนี้ฉันต้องการประกาศโครงสร้างchild_tที่มีขนาดเท่ากับparent_t.text.

ฉันจะทำเช่นนี้ได้อย่างไร? (รหัสหลอกด้านล่าง)

typedef struct _child
{
  char flag ;
  char text[sizeof(parent_t.text)] ;
  int used ;
} child_t ;

ฉันลองหลายวิธีด้วยparent_tและstruct _parentแต่คอมไพเลอร์ของฉันไม่ยอมรับ

เคล็ดลับนี้ดูเหมือนจะใช้ได้:

parent_t* dummy ;
typedef struct _child
{
  char flag ;
  char text[sizeof(dummy->text)] ;
  int used ;
} child_t ;

เป็นไปได้ไหมที่จะประกาศchild_tโดยไม่ต้องใช้dummy?

คำตอบ:


202

แม้ว่าการกำหนดขนาดบัฟเฟอร์ด้วย a #defineจะเป็นวิธีการใช้สำนวนวิธีหนึ่ง แต่อีกวิธีหนึ่งก็คือการใช้มาโครเช่นนี้:

#define member_size(type, member) sizeof(((type *)0)->member)

และใช้มันดังนี้:

typedef struct
{
    float calc;
    char text[255];
    int used;
} Parent;

typedef struct
{
    char flag;
    char text[member_size(Parent, text)];
    int used;
} Child;

ฉันรู้สึกประหลาดใจเล็กน้อยที่sizeof((type *)0)->member)อนุญาตให้ใช้เป็นนิพจน์คงที่ได้ สิ่งที่เย็น.


2
ว้าวฉันไม่รู้ว่า sizeof ((type *) 0) -> member) ใช้ได้ผล ตอนนี้ฉันไม่ได้อยู่ในเครื่อง dev ของฉัน แต่มันใช้ได้กับคอมไพเลอร์ทั้งหมดหรือไม่ ขอบคุณสำหรับ Joey ที่
Gangadhar

4
@Gangadhar: ใช่มันใช้ได้กับคอมไพเลอร์ทั้งหมด ตัวถูกดำเนินการของsizeofไม่ได้รับการประเมินดังนั้นจึงไม่มีปัญหากับการยกเลิกการอ้างอิงตัวชี้ค่าว่าง (เนื่องจากไม่ได้ถูกอ้างถึงจริง)
James McNellis

4
วิเศษ? C89 ธรรมดาดูการใช้งาน "offsetof" ใน <stddef.h> หรือการใช้งานเดียวกันeetimes.com/design/other/4024941/…
user411313

1
@XavierGeoffrey: ที่นี่ sizeof อนุมานประเภทของสมาชิก ((type *) 0) -> และส่งกลับขนาดของประเภทนั้น ((type *) 0) เป็นเพียงตัวชี้ค่าว่างของประเภทโครงสร้าง ptr-> member คือ (ในเวลาคอมไพล์) ซึ่งเป็นนิพจน์ที่มีประเภทของสมาชิก รหัสภายใน sizeof ไม่เคยทำงาน (ถ้าเป็นเช่นนั้นโปรแกรมจะแยกส่วน!) ดูเฉพาะประเภทของค่าภายใน sizeof เท่านั้น
Joey Adams

1
หน้าวิกิพีเดีย offset_ofบันทึกนี้เป็นพฤติกรรมที่ไม่ได้กำหนดตามมาตรฐาน C ดังนั้นฉันจะไม่เดิมพันกับมันทำงานร่วมกับคอมไพเลอร์ทั้งหมด
Graeme

30

ฉันไม่ได้อยู่ในเครื่องพัฒนาของฉันในตอนนี้ แต่ฉันคิดว่าคุณสามารถทำอย่างใดอย่างหนึ่งต่อไปนี้:

sizeof(((parent_t *)0)->text)

sizeof(((parent_t){0}).text)


แก้ไข : ฉันชอบมาโคร member_size ที่ Joey แนะนำให้ใช้เทคนิคนี้ฉันคิดว่าฉันจะใช้มัน


12
รูปแบบที่สองนั้นดีมาก (และมีแนวคิดที่สะอาดโดยที่ไม่เกี่ยวข้องกับพอยน์เตอร์ว่าง) แต่คุณควรระบุว่าเป็นไปไม่ได้ในคอมไพเลอร์ก่อน C99
R .. GitHub STOP HELPING ICE

11

คุณมีอิสระที่จะใช้FIELD_SIZEOF(t, f)ในเคอร์เนล Linux มีการกำหนดไว้ดังนี้:

#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))

มาโครประเภทนี้ถูกกล่าวถึงในคำตอบอื่น ๆ แต่สามารถพกพาได้มากกว่าเมื่อใช้มาโครที่กำหนดไว้แล้ว


4
FIELD_SIZEOF เป็นมาโครที่ใช้ในเคอร์เนล Linux สมัยใหม่ซึ่งพบใน linux / kernel.h
Colin Ian King

@ColinIanKing ดังนั้นนี่เป็นวิธีที่ใช้สำนวนใน C โดยทั่วไปเพื่อให้ได้ขนาดของสมาชิก struct? มาโครดังกล่าวยังไม่รวมอยู่ใน Standard ใน C สมัยใหม่ใช่หรือไม่
เซนต์แอนตาริโอ

สำหรับความรู้ของฉันไม่มีมาโครที่เทียบเท่าในมาตรฐาน C.
โคลินเอียนคิง

เพิ่งถูกลบออกจาก linux / kernel.h
อังเดร Makukha

10

ใช้คำสั่งพรีโปรเซสเซอร์เช่น #define:

#define TEXT_LEN 255

typedef struct _parent
{
  float calc ;
  char text[TEXT_LEN] ;
  int used ;
} parent_t ;

typedef struct _child
{
  char flag ;
  char text[TEXT_LEN] ;
  int used ;
} child_t ;

เห็นด้วยกับ Nyan วิธีแก้ปัญหาอื่น ๆ ใช้ได้ผล แต่อย่าทำอะไรที่แตกต่างออกไป หากคุณต้องการให้ชัดเจนยิ่งขึ้นเรียกว่า PARENT_TEXT_LEN หรือสิ่งที่สื่อความหมายได้เท่าเทียมกัน จากนั้นคุณสามารถใช้ในเงื่อนไขตลอดทั้งรหัสของคุณเพื่อป้องกันข้อผิดพลาดเกี่ยวกับความยาวบัฟเฟอร์และจะทำการเปรียบเทียบจำนวนเต็มอย่างง่าย
dave mankoff

1
ฉันคิดว่าข้อดีของโซลูชัน sizeof คือคุณไม่จำเป็นต้องเข้าถึงโครงสร้างหลักหากมีการกำหนดไว้ในไลบรารีหรือที่อื่น

@evilclown: แต่คุณทำ: "char text [member_size (Parent, text)];" คุณต้องอ้างอิง "ผู้ปกครอง"
dave mankoff

3
ฉันไม่คิดว่าคุณเข้าใจฉัน สมมติว่า struct ผู้ปกครองที่มีdrand48_data(ที่กำหนดไว้ใน stdlib.h) และคุณต้องการ __xsizeof คุณไม่สามารถ#define X_LEN 3และเปลี่ยน stdlib.h ได้การใช้member_sizeจะดีกว่าเมื่อคุณไม่สามารถเข้าถึงแหล่งที่มาของโครงสร้างพาเรนต์ซึ่งดูเหมือนว่าเป็นสถานการณ์ที่สมเหตุสมผล

5

คุณสามารถใช้คำสั่งพรีโปรเซสเซอร์สำหรับขนาดเป็น:

#define TEXT_MAX_SIZE 255

และใช้ทั้งในผู้ปกครองและเด็ก


ใช่ แต่มันไม่ใช่ตัวเลือกเสมอไป: ใน linux /usr/include/bits/dirent.hขนาดของd_nameฟิลด์ (struct direct/ dirent) ไม่ได้ถูกกำหนดให้เป็นDIRSIZอีกต่อไป แต่เป็นแบบฮาร์ดโค้ด จึงsizeof()น่าจะเป็นวิธีเดียวที่สะอาดเพื่อให้พึ่งพา
ジョージ

5

struct.h ได้กำหนดไว้แล้ว

#define fldsiz(name, field) \
    (sizeof(((struct name *)0)->field))

คุณก็ทำได้

#include <stdlib.h> /* EXIT_SUCCESS */
#include <stdio.h>  /* printf */
#include <struct.h> /* fldsiz */

struct Penguin {
    char name[128];
    struct Penguin *child[16];
};
static const int name_size  = fldsiz(Penguin, name) / sizeof(char);
static const int child_size = fldsiz(Penguin, child) / sizeof(struct Penguin *);

int main(void) {
    printf("Penguin.name is %d chars and Penguin.child is %d Penguin *.\n",
           name_size, child_size);
    return EXIT_SUCCESS;
}

แต่เมื่อดูในส่วนหัวดูเหมือนว่านี่เป็นสิ่งที่ BSD ไม่ใช่มาตรฐาน ANSI หรือ POSIX ฉันลองใช้กับเครื่อง Linux แล้วไม่ได้ผล ประโยชน์ที่ จำกัด


4

ความเป็นไปได้อีกประการหนึ่งคือการกำหนดประเภท ความจริงที่ว่าคุณต้องการให้แน่ใจว่าทั้งสองฟิลด์มีขนาดเท่ากันเป็นตัวบ่งชี้ว่าคุณมีความหมายเหมือนกันสำหรับพวกเขาฉันคิดว่า

typedef char description[255];

แล้วมีฟิลด์

description text;

ในทั้งสองประเภทของคุณ


4

โซลูชัน c ++:

sizeof (Type :: member) ดูเหมือนจะใช้งานได้เช่นกัน:

struct Parent
{
    float calc;
    char text[255];
    int used;
};

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};

3
คำถามเกี่ยวกับ C ไม่ใช่ C ++
Marc

0

@ joey-adams ขอบคุณ! ฉันกำลังค้นหาสิ่งเดียวกัน แต่สำหรับอาร์เรย์ที่ไม่ใช่ถ่านและทำงานได้ดีอย่างสมบูรณ์แบบแม้ในลักษณะนี้:

#define member_dim(type, member) sizeof(((type*)0)->member) / \
                                 sizeof(((type*)0)->member[0])

struct parent {
        int array[20];
};

struct child {
        int array[member_dim(struct parent, array)];
};

int main ( void ) {
        return member_dim(struct child, array);
}

ผลตอบแทน 20 ตามที่คาดไว้

และ @ brandon-horsley มันก็ใช้ได้ดีเช่นกัน:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.