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.