sizeof thành viên cấu trúc đơn trong C


109

Tôi đang cố gắng khai báo một cấu trúc phụ thuộc vào một cấu trúc khác. Tôi muốn sử dụng sizeofđể được an toàn / đáng tin cậy.

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

Bây giờ tôi muốn khai báo một cấu trúc child_tcó cùng kích thước với parent_t.text.

Tôi có thể làm cái này như thế nào? (Mã giả bên dưới.)

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

Tôi đã thử một vài cách khác nhau với parent_tstruct _parent, nhưng trình biên dịch của tôi sẽ không chấp nhận.

Như một mẹo nhỏ, điều này dường như hoạt động:

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

Có thể khai báo child_tmà không sử dụng dummy?

Câu trả lời:


202

Mặc dù việc xác định kích thước vùng đệm bằng a #definelà một cách khá dễ hiểu, nhưng một cách khác sẽ là sử dụng macro như sau:

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

và sử dụng nó như thế này:

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

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

Tôi thực sự hơi ngạc nhiên rằng sizeof((type *)0)->member)nó thậm chí còn được cho phép như một biểu thức không đổi. Những thứ tuyệt vời.


2
Chà, tôi không biết sizeof ((type *) 0) -> member) hoạt động. Bây giờ không có trên máy phát triển của tôi, nhưng điều này có hoạt động với tất cả các trình biên dịch không? Cảm ơn vì Joey đó.
Gangadhar

4
@Gangadhar: Có, điều này hoạt động cho tất cả các trình biên dịch. Toán hạng của sizeofkhông được đánh giá, vì vậy không có vấn đề gì với việc bỏ tham chiếu đến con trỏ null (vì nó không thực sự được tham chiếu).
James McNellis,

4
Tuyệt vời? C89 đơn giản của nó, xem việc thực hiện "offsetof" trong <stddef.h> hoặc thực hiện cùng eetimes.com/design/other/4024941/...
user411313

1
@XavierGeoffrey: Ở đây, sizeof suy ra kiểu ((type *) 0) -> thành viên và trả về kích thước của kiểu đó. ((type *) 0) chỉ là một con trỏ null của kiểu struct. ptr-> thành viên là (tại thời điểm biên dịch) một biểu thức có kiểu của thành viên. Mã trong sizeof không bao giờ chạy (nếu có, chương trình sẽ mặc định!). Chỉ loại giá trị trong sizeof mới được xem xét.
Joey Adams

1
Các trang Wikipedia cho offset_of ghi chú này là hành vi không xác định theo tiêu chuẩn C, vì vậy tôi sẽ không đặt cược vào nó làm việc với tất cả các trình biên dịch.
Graeme

30

Tôi không ở trên máy phát triển của mình ngay bây giờ, nhưng tôi nghĩ bạn có thể thực hiện một trong những cách sau:

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

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


Chỉnh sửa : Tôi thích macro member_size mà Joey đề xuất sử dụng kỹ thuật này, tôi nghĩ tôi sẽ sử dụng nó.


12
Dạng thứ hai rất hay (và sạch về mặt khái niệm ở chỗ nó không liên quan đến con trỏ rỗng), nhưng bạn nên đề cập rằng nó không thể thực hiện được trong các trình biên dịch trước C99.
R .. GitHub DỪNG TRỢ GIÚP LÚC NỮA,

11

Bạn có thể sử dụng miễn phí FIELD_SIZEOF(t, f)trong nhân Linux. Nó chỉ được định nghĩa như sau:

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

Loại macro này được đề cập trong các câu trả lời khác. Nhưng sẽ dễ di chuyển hơn khi sử dụng macro đã được xác định.


4
FIELD_SIZEOF là vĩ mô được sử dụng trong Linux hiện đại hạt nhân, được tìm thấy trong linux / kernel.h
Colin Ian Vua

@ColinIanKing Vì vậy, đây là một cách thành ngữ trong C nói chung để có được kích thước thành viên struct? Một macro như vậy không được bao gồm trong một Tiêu chuẩn trong C hiện đại chưa?
St.Antario

Theo hiểu biết của tôi không có vĩ mô tương đương trong C. tiêu chuẩn
Colin Ian Vua

Gần đây nó đã bị xóa khỏi linux / kernel.h.
Andriy Makukha

10

Sử dụng chỉ thị tiền xử lý, tức là #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 ;

Đồng ý với Nyan. Các giải pháp khác hoạt động, nhưng không đạt được bất kỳ điều gì khác biệt. Nếu bạn muốn rõ ràng hơn, hãy gọi nó là PARENT_TEXT_LEN hoặc một cái gì đó mô tả tương tự. Sau đó, bạn cũng có thể sử dụng nó trong các điều kiện trong toàn bộ mã của mình để tránh lỗi độ dài bộ đệm và nó sẽ thực hiện các phép so sánh số nguyên đơn giản.
dave mankoff

1
tôi nghĩ rằng lợi thế của giải pháp sizeof là bạn không cần truy cập vào cấu trúc mẹ, nếu nó được xác định trong thư viện hoặc một nơi nào khác.

@evilclown: nhưng bạn làm: "char text [member_size (Parent, text)];" Bạn cần tham chiếu đến "Parent".
dave mankoff

3
Tôi không nghĩ rằng bạn hiểu tôi. giả sử cấu trúc mẹ là drand48_data(được định nghĩa trong stdlib.h) và bạn muốn kích thước của nó __x. bạn không thể #define X_LEN 3và thay đổi stdlib.h, việc sử dụng member_sizesẽ tốt hơn khi bạn không có quyền truy cập vào nguồn của cấu trúc mẹ, điều này có vẻ như là một tình huống hợp lý.

5

Bạn có thể sử dụng chỉ thị tiền xử lý cho kích thước như:

#define TEXT_MAX_SIZE 255

và sử dụng nó cho cả cha và con.


vâng, nhưng nó không phải lúc nào cũng là một tùy chọn: giả sử, trong linux /usr/include/bits/dirent.h, kích thước của d_nametrường (struct direct/ dirent) không được định nghĩa là DIRSIZbất kỳ nữa nhưng được mã hóa cứng; vì vậy sizeof()dường như là cách sạch sẽ duy nhất để giữ sự phụ thuộc
ジ ョ ー ジ

5

struct.h chúng đã được xác định chưa,

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

vì vậy bạn có thể,

#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;
}

nhưng, khi nhìn vào tiêu đề, có vẻ như đây là thứ BSD chứ không phải tiêu chuẩn ANSI hoặc POSIX. Tôi đã thử nó trên một máy Linux và nó không hoạt động; tính hữu dụng hạn chế.


4

Một khả năng khác là xác định một kiểu. Tôi nghĩ rằng việc bạn muốn đảm bảo cùng một kích thước cho hai trường là một dấu hiệu cho thấy bạn có cùng ngữ nghĩa cho chúng.

typedef char description[255];

và sau đó có một lĩnh vực

description text;

trong cả hai loại của bạn.


4

giải pháp c ++:

sizeof (Loại :: thành viên) dường như cũng đang hoạt động:

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

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

3
Câu hỏi là về C, không phải C ++.
Marc

0

@ joey-adams, cảm ơn bạn! Tôi đã tìm kiếm điều tương tự, nhưng đối với mảng không phải char và nó hoạt động hoàn toàn tốt ngay cả theo cách này:

#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);
}

Nó trả về 20 như mong đợi.

Và, @ brandon-horseley, điều này cũng hoạt động tốt:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.