dimensione del singolo membro della struttura in C


109

Sto cercando di dichiarare una struttura che dipende da un'altra struttura. Voglio usare sizeofper essere sicuro / pedante.

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

Ora voglio dichiarare una struttura child_tche ha la stessa dimensione di parent_t.text.

Come posso fare questo? (Pseudo-codice di seguito.)

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

Ho provato diversi modi con parent_te struct _parent, ma il mio compilatore non accetta.

Come trucco, sembra funzionare:

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

È possibile dichiarare child_tsenza l'uso di dummy?

Risposte:


202

Sebbene definire la dimensione del buffer con a #definesia un modo idiomatico per farlo, un altro sarebbe usare una macro come questa:

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

e usalo in questo modo:

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

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

In realtà sono un po 'sorpreso che sizeof((type *)0)->member)sia persino consentita come espressione costante. Roba forte.


2
Wow, non sapevo che sizeof ((type *) 0) -> member) funzionasse. Non sono sulla mia macchina di sviluppo adesso, ma funziona per tutti i compilatori? Grazie per questo Joey.
— Gangadhar

4
@ Gangadhar: Sì, funziona per tutti i compilatori. L'operando di sizeofnon viene valutato, quindi non ci sono problemi con la dereferenziazione del puntatore null (perché non è effettivamente dereferenziata).
— James McNellis

4
meraviglioso? è semplice C89, vedere l'implementazione di "offsetof" in <stddef.h> o la stessa implementazione eetimes.com/design/other/4024941/…
— user411313

1
@XavierGeoffrey: qui, sizeof deduce il tipo di ((type *) 0) -> membro e restituisce la dimensione di quel tipo. ((type *) 0) è solo un puntatore nullo del tipo struct. ptr-> member è (in fase di compilazione) un'espressione il cui tipo è quello del membro. Il codice all'interno di sizeof non viene mai eseguito (se lo facesse, il programma sarebbe segfault!). Viene considerato solo il tipo di valore all'interno di sizeof.
— Joey Adams,

1
La pagina di Wikipedia per offset_of lo nota come un comportamento indefinito secondo lo standard C, quindi non scommetterei sul fatto che funzioni con tutti i compilatori.
— Graeme

30

Al momento non sono sulla mia macchina di sviluppo, ma penso che tu possa eseguire una delle seguenti operazioni:

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

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


Modifica : mi piace la macro member_size che Joey ha suggerito di usare questa tecnica, penso che la userei.


12
La seconda forma è molto carina (e concettualmente pulita in quanto non coinvolge puntatori nulli), ma dovresti menzionare che non è possibile nei compilatori precedenti al C99.
— R .. GitHub SMETTA DI AIUTARE ICE

11

Sei libero di usare FIELD_SIZEOF(t, f)nel kernel Linux. È semplicemente definito come segue:

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

Questo tipo di macro è menzionato in altre risposte. Ma è più portabile usare una macro già definita.


4
FIELD_SIZEOF è la macro utilizzata nei moderni kernel Linux, che si trova in linux / kernel.h
— Colin Ian King

@ColinIanKing Quindi questo è un modo idiomatico in C in generale per ottenere la dimensione del membro della struttura? Una tale macro non è ancora inclusa in uno Standard nel C moderno?
— Sant'Antario

Per quanto ne so non esiste una macro equivalente nello standard C.
— Colin Ian King

È stato recentemente rimosso da linux / kernel.h.
— Andriy Makukha

10

Usa una direttiva del preprocessore, ad esempio #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 ;

D'accordo con Nyan. Le altre soluzioni funzionano, ma non realizzano nulla di diverso. Se vuoi essere più esplicito, chiamalo PARENT_TEXT_LEN o qualcosa di altrettanto descrittivo. È quindi possibile utilizzarlo anche in condizionali in tutto il codice per evitare errori di lunghezza del buffer e farà semplici confronti di interi.
— dave mankoff

1
Penso che il vantaggio della soluzione sizeof sia che non è necessario accedere alla struttura genitore, se è definita in una libreria o da qualche altra parte.

@evilclown: ma lo fai: "char text [member_size (Parent, text)];" Devi fare riferimento al "Genitore".
— dave mankoff

3
Non credo che tu mi abbia capito. supponiamo che la struttura genitore sia drand48_data(definita in stdlib.h) e che tu voglia la dimensione di __x. non è possibile #define X_LEN 3e modificare stdlib.h, l'uso member_sizeè superiore quando non si ha accesso al sorgente della struttura padre, il che sembra una situazione ragionevole.

5

È possibile utilizzare una direttiva del preprocessore per la dimensione come:

#define TEXT_MAX_SIZE 255

e usalo sia nel genitore che nel figlio.


sì, ma non è sempre un'opzione: diciamo, in linux /usr/include/bits/dirent.hla dimensione del d_namecampo (struct direct/ dirent) non è più definita come DIRSIZhard-coded; quindi sizeof()sembra essere l'unico modo pulito per mantenere la dipendenza
— ジ ョ ー ジ

5

struct.h li ha già definiti,

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

così potresti

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

ma, guardando nell'intestazione, sembra che questa sia una cosa BSD e non standard ANSI o POSIX. L'ho provato su una macchina Linux e non ha funzionato; utilità limitata.


4

Un'altra possibilità sarebbe definire un tipo. Il fatto che tu voglia garantire la stessa dimensione per i due campi è un indicatore del fatto che hai la stessa semantica per loro, credo.

typedef char description[255];

e poi avere un campo

description text;

in entrambi i tuoi tipi.


4

soluzione c ++:

Anche sizeof (Type :: member) sembra funzionare:

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

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

3
La domanda riguarda C, non C ++.
— Marc

0

@ joey-adams, grazie! Stavo cercando la stessa cosa, ma per array non char e funziona perfettamente anche in questo modo:

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

Restituisce 20 come previsto.

E, @ brandon-horsley, anche questo funziona bene:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.