sizeof pojedynczego elementu struktury w C

109

Próbuję zadeklarować strukturę, która jest zależna od innej struktury. Chcę sizeofbyć bezpieczny / pedantyczny.

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

Teraz chcę zadeklarować strukturę, child_tktóra ma taki sam rozmiar jak parent_t.text.

W jaki sposób mogę to zrobić? (Pseudokod poniżej).

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

Próbowałem na kilka różnych sposobów z parent_ti struct _parent, ale mój kompilator nie akceptuje.

Wydaje się, że to działa:

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

Czy można złożyć deklarację child_tbez użycia dummy?

kevinarpe
źródło

Odpowiedzi:

202

Chociaż zdefiniowanie rozmiaru bufora za pomocą a #definejest idiomatycznym sposobem, aby to zrobić, innym byłoby użycie makra takiego:

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

i użyj go w ten sposób:

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

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

Właściwie jestem trochę zaskoczony, że sizeof((type *)0)->member)jest to dozwolone nawet jako ciągłe wyrażenie. Fajne rzeczy.

Joey Adams
źródło
2
Wow, nie wiedziałem, że sizeof ((typ *) 0) -> członek) działa. Nie jestem teraz na mojej maszynie deweloperskiej, ale czy to działa dla wszystkich kompilatorów? Dzięki za to Joey.
Gangadhar
4
@Gangadhar: Tak, to działa dla wszystkich kompilatorów. Argument operandu sizeofnie jest oceniany, więc nie ma problemu z wyłuskiwaniem wskaźnika pustego (ponieważ w rzeczywistości nie jest on wyłuskiwany).
James McNellis
4
wspaniale? jego zwykły C89, patrz implementacja "offsetof" w <stddef.h> lub ta sama implementacja eetimes.com/design/other/4024941/ ...
user411313
1
@XavierGeoffrey: Tutaj sizeof wnioskuje o typ ((type *) 0) -> element członkowski i zwraca rozmiar tego typu. ((type *) 0) jest po prostu pustym wskaźnikiem typu struktury. ptr-> member jest (w czasie kompilacji) wyrażeniem, którego typem jest element członkowski. Kod w ramach sizeof nigdy się nie uruchamia (gdyby tak się stało, program by się segfaultował!). Pod uwagę brany jest tylko typ wartości w ramach sizeof.
Joey Adams
1
Strona Wikipedii dotycząca offset_of zauważa to jako niezdefiniowane zachowanie zgodnie ze standardem C, więc nie założyłbym się , że będzie działać ze wszystkimi kompilatorami.
Graeme
30

Nie jestem teraz na moim komputerze deweloperskim, ale myślę, że możesz wykonać jedną z następujących czynności:

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

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


Edycja : Podoba mi się makro member_size, który Joey zasugerował użycie tej techniki, myślę, że użyłbym tego.

Brandon Horsley
źródło
12
Druga forma jest bardzo ładna (i koncepcyjnie czysta, ponieważ nie zawiera wskaźników zerowych), ale należy wspomnieć, że nie jest to możliwe w kompilatorach sprzed C99.
R .. GitHub PRZESTAŃ POMÓC W LODZIE
11

Możesz swobodnie używać FIELD_SIZEOF(t, f)w jądrze Linuksa. Jest zdefiniowany w następujący sposób:

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

Ten typ makra jest wymieniany w innych odpowiedziach. Ale bardziej przenośne jest użycie już zdefiniowanego makra.

Dmitrij
źródło
4
FIELD_SIZEOF to makro używane w nowoczesnych jądrach Linuksa, które można znaleźć w linux / kernel.h
Colin Ian King
@ColinIanKing Więc jest to idiomatyczny sposób w C w ogóle, aby uzyskać rozmiar elementu struktury? Czy takie makro nie jest jeszcze zawarte w standardzie w nowoczesnym C?
St.Antario
O ile mi wiadomo, nie ma odpowiednika makra w standardzie C.
Colin Ian King,
Niedawno został usunięty z linux / kernel.h.
Andriy Makukha
10

Użyj dyrektywy preprocesora, tj. #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 ;
dave mankoff
źródło
Zgadzam się z Nyanem. Inne rozwiązania działają, ale nie zapewniają niczego innego. Jeśli chcesz być bardziej wyraźny, nazwij to PARENT_TEXT_LEN lub coś równie opisowego. Następnie możesz go również użyć w kodach warunkowych w całym kodzie, aby zapobiec błędom długości bufora i będzie wykonywać proste porównania liczb całkowitych.
dave mankoff
1
Myślę, że zaletą rozwiązania sizeof jest to, że nie potrzebujesz dostępu do struktury nadrzędnej, jeśli jest zdefiniowana w bibliotece lub w innym miejscu.
@evilclown: ale robisz: "char text [member_size (Parent, text)];" Musisz odwołać się do „Parent”.
dave mankoff
3
Myślę, że mnie nie zrozumiałeś. załóżmy, że struktura nadrzędna jest drand48_data(zdefiniowana w stdlib.h) i chcesz, aby rozmiar sizeof __x. nie możesz #define X_LEN 3i zmienić stdlib.h, używanie member_sizejest lepsze, gdy nie masz dostępu do źródła struktury macierzystej, co wydaje się rozsądną sytuacją.
5

Możesz użyć dyrektywy preprocesora dla rozmiaru jako:

#define TEXT_MAX_SIZE 255

i używaj go zarówno u rodzica, jak i dziecka.

codaddict
źródło
tak, ale nie zawsze jest to opcja: powiedzmy, w Linuksie /usr/include/bits/dirent.hrozmiar d_namepola (struct direct/ dirent) nie jest już zdefiniowany jako DIRSIZjuż zakodowany; więc sizeof()wydaje się być jedynym czystym sposobem na utrzymanie zależności
ジ ョ ー ジ
5

struct.h ma je już zdefiniowane,

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

więc mógłbyś

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

ale patrząc w nagłówek, okazuje się, że jest to sprawa BSD, a nie standard ANSI czy POSIX. Wypróbowałem to na komputerze z Linuksem i nie działało; ograniczona przydatność.

Neil
źródło
4

Inną możliwością byłoby zdefiniowanie typu. Fakt, że chcesz zapewnić ten sam rozmiar dla dwóch pól, jest wskaźnikiem, że masz dla nich taką samą semantykę, jak sądzę.

typedef char description[255];

a potem mieć pole

description text;

w obu twoich typach.

Jens Gustedt
źródło
4

rozwiązanie C ++:

Wygląda na to, że sizeof (Type :: member) również działa:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
korish
źródło
3
Pytanie dotyczy C, a nie C ++.
Marc
0

@ joey-adams, dziękuję! Szukałem tego samego, ale dla tablicy non char i działa doskonale nawet w ten sposób:

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

Zwraca 20 zgodnie z oczekiwaniami.

@ Brandon-horsley, to też działa dobrze:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
Roman Kovtuh
źródło