taille d'un membre de structure unique en C

109

J'essaye de déclarer une structure qui dépend d'une autre structure. Je veux utiliser sizeofpour être sûr / pédant.

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

Maintenant, je veux déclarer une structure child_tqui a la même taille que parent_t.text.

Comment puis-je faire ceci? (Pseudo-code ci-dessous.)

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

J'ai essayé différentes méthodes avec parent_tet struct _parent, mais mon compilateur n'acceptera pas.

En guise d'astuce, cela semble fonctionner:

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

Est-il possible de déclarer child_tsans utiliser de dummy?

kevinarpe
la source

Réponses:

202

Bien que définir la taille de la mémoire tampon avec un #definesoit une façon idiomatique de le faire, une autre serait d'utiliser une macro comme celle-ci:

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

et utilisez-le comme ceci:

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

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

Je suis en fait un peu surpris que ce sizeof((type *)0)->member)soit même autorisé comme expression constante. Truc cool.

Joey Adams
la source
2
Wow, je ne savais pas que sizeof ((type *) 0) -> membre) fonctionne. Je ne suis pas sur ma machine de développement maintenant, mais cela fonctionne-t-il pour tous les compilateurs? Merci pour ça Joey.
Gangadhar
4
@Gangadhar: Oui, cela fonctionne pour tous les compilateurs. L'opérande de sizeofn'est pas évalué, il n'y a donc aucun problème avec le déréférencement du pointeur nul (car il n'est pas réellement déréférencé).
James McNellis
4
formidable? son simple C89, voir l'implémentation de "offsetof" dans <stddef.h> ou la même implémentation eetimes.com/design/other/4024941/…
user411313
1
@XavierGeoffrey: Ici, sizeof déduit le type de ((type *) 0) -> membre, et renvoie la taille de ce type. ((type *) 0) est juste un pointeur nul du type struct. ptr-> membre est (au moment de la compilation) une expression dont le type est celui du membre. Le code dans la taille de ne s'exécute jamais (si c'était le cas, le programme ferait une erreur de segmentation!). Seul le type de valeur compris dans sizeof est examiné.
Joey Adams le
1
La page Wikipedia pour offset_of note qu'il s'agit d'un comportement indéfini selon le standard C, donc je ne parierais pas qu'il fonctionne avec tous les compilateurs.
Graeme
30

Je ne suis pas sur ma machine de développement pour le moment, mais je pense que vous pouvez effectuer l'une des opérations suivantes:

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

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


Edit : J'aime la macro member_size que Joey a suggérée en utilisant cette technique, je pense que je l'utiliserais.

Brandon Horsley
la source
12
La deuxième forme est très agréable (et conceptuellement propre en ce qu'elle n'implique pas de pointeurs nuls), mais vous devez mentionner que ce n'est pas possible dans les compilateurs pré-C99.
R .. GitHub STOP HELPING ICE
11

Vous êtes libre d'utiliser FIELD_SIZEOF(t, f)dans le noyau Linux. Il est simplement défini comme suit:

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

Ce type de macro est mentionné dans d'autres réponses. Mais il est plus portable d'utiliser une macro déjà définie.

Dmitry
la source
4
FIELD_SIZEOF est la macro utilisée dans les noyaux Linux modernes, trouvée dans linux / kernel.h
Colin Ian King
@ColinIanKing C'est donc une manière idiomatique en C en général d'obtenir la taille du membre de la structure? Une telle macro n'est-elle pas encore incluse dans un standard du C moderne?
St.Antario
À ma connaissance, il n'y a pas de macro équivalente dans la norme C.
Colin Ian King
Il a été récemment supprimé de linux / kernel.h.
Andriy Makukha
10

Utilisez une directive de préprocesseur, c'est-à-dire #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
la source
D'accord avec Nyan. Les autres solutions fonctionnent, mais n'accomplissent rien de différent. Si vous voulez être plus explicite, appelez-le PARENT_TEXT_LEN ou quelque chose de tout aussi descriptif. Vous pouvez également l'utiliser dans des conditions tout au long de votre code pour éviter les erreurs de longueur de tampon et il fera de simples comparaisons entières.
dave mankoff
1
Je pense que l'avantage de la solution sizeof est que vous n'avez pas besoin d'accéder à la structure parent, si elle est définie dans une bibliothèque ou ailleurs.
@evilclown: mais vous faites: "char text [member_size (Parent, text)];" Vous devez référencer le "Parent".
dave mankoff
3
je ne pense pas que tu m'as compris. supposons que la structure parent soit drand48_data(définie dans stdlib.h) et que vous vouliez la taille de __x. vous ne pouvez pas #define X_LEN 3et changez le stdlib.h, l'utilisation member_sizeest supérieure lorsque vous n'avez pas accès à la source de la structure parent, ce qui semble être une situation raisonnable.
5

Vous pouvez utiliser une directive de préprocesseur pour la taille comme:

#define TEXT_MAX_SIZE 255

et utilisez-le à la fois chez le parent et l'enfant.

codaddict
la source
oui, mais ce n'est pas toujours une option: disons, sous Linux, /usr/include/bits/dirent.hla taille du d_namechamp (struct direct/ dirent) n'est plus définie comme étant DIRSIZcodée en dur; donc sizeof()semble être la seule façon de garder propre la dépendance
ジョージ
5

struct.h les a déjà définis,

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

pour que tu puisses,

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

mais, en regardant dans l'en-tête, il semble que ce soit une chose BSD et non ANSI ou POSIX standard. Je l'ai essayé sur une machine Linux et cela n'a pas fonctionné; utilité limitée.

Neil
la source
4

Une autre possibilité serait de définir un type. Le fait que vous vouliez assurer la même taille pour les deux champs est un indicateur que vous avez la même sémantique pour eux, je pense.

typedef char description[255];

puis avoir un champ

description text;

dans vos deux types.

Jens Gustedt
la source
4

solution c ++:

sizeof (Type :: member) semble également fonctionner:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
coréen
la source
3
La question concerne le C, pas le C ++.
Marc
0

@ joey-adams, merci! Je cherchais la même chose, mais pour un tableau non char et cela fonctionne parfaitement bien même de cette façon:

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

Il renvoie 20 comme prévu.

Et, @ brandon-horsley, cela fonctionne bien aussi:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
Roman Kovtuh
la source