2008-11-17 7 views
48

Je travaille sur la refactorisation d'un ancien code et j'ai trouvé quelques structures contenant des tableaux de longueur nulle (ci-dessous). Avertissements déprimés par pragma, bien sûr, mais j'ai échoué à créer par de «nouvelles» structures contenant de telles structures (erreur 2233). Array 'byData' utilisé comme pointeur, mais pourquoi ne pas utiliser pointer à la place? ou un tableau de longueur 1? Et bien sûr, aucun commentaire n'a été ajouté pour me faire profiter du processus ... Toutes les causes d'utiliser une telle chose? Des conseils pour les refactoriser?Tableau de longueur

struct someData 
{ 
    int nData; 
    BYTE byData[0]; 
} 

NB Il est C++, Windows XP, VS 2003

+3

Ceci est le "struct hack", décrit à la question 2.6 de la [FAQ comp.lang.c] (http://www.c-faq.com/). Dennis Ritchie l'a qualifié de «chumminess injustifié avec la mise en œuvre du C». C99 a introduit une nouvelle fonctionnalité de langage, le "membre de tableau flexible", pour remplacer le hack struct. Même le compilateur de Microsoft, qui est noté pour son manque de prise en charge de C99, prend en charge les membres de groupe flexibles. –

+0

N'ajoutez PAS le tag 'c' à cette question.Les règles C++ pour cela sont assez différentes des règles C. –

+0

@BenVoigt La réponse acceptée est le code C pur, donc je suppose que votre édition est erronée. c hack s'applique à c et C++ de la même manière –

Répondre

33

Oui, il s'agit d'un C-Hack.
Pour créer un tableau de longueur quelconque:

struct someData* mallocSomeData(int size) 
{ 
    struct someData* result = (struct someData*)malloc(sizeof(struct someData) + size * sizeof(BYTE)); 
    if (result) 
    { result->nData = size; 
    } 
    return result; 
} 

Maintenant vous avez un objet de someData avec un tableau d'une longueur spécifiée.

+0

Ne devrait-il pas au moins utiliser 'new []', ceci étant à propos de C++? – unwind

+1

@unwind: Impossible d'utiliser new pour cela. Tout le problème est que c'est un C-Hack et pas nécessaire en C++ (parce que nous avons de meilleures façons de le faire). Aussi je suis assez sûr que les tableaux de longueur nulle sont illégaux en C++ (enfin au moins C++ 03, pas sûr si cela a été mis à jour en C++ 11). –

+0

Le jargon populaire pour cela est "Struct Hack". –

22

Ceci est un vieux C hack pour permettre à une taille des tableaux flexibles.

Dans la norme C99 ce n'est pas nécessaire car il supporte la syntaxe arr [].

+3

Malheureusement, Visual Studio est très médiocre en ce qui concerne le support C99. :( –

+5

Sans tenir compte de la vérité générale de votre commentaire, ... le compilateur MS VC v9 supporte la syntaxe arr [] – Cheeso

23

Il existe, malheureusement, plusieurs raisons pour déclarer un tableau de longueur nulle à la fin d'une structure. Il vous donne essentiellement la possibilité d'avoir une structure de longueur variable renvoyée par une API.

Raymond Chen a fait un excellent article de blog sur le sujet. Je vous suggère de jeter un coup d'œil à cet article car il contient probablement la réponse que vous voulez. Remarque dans son article, il traite des tableaux de taille 1 au lieu de 0. C'est le cas parce que les tableaux de longueur nulle sont une entrée plus récente dans les normes. Son message devrait toujours s'appliquer à votre problème.

http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx

EDIT

Note: Même si le poste de Raymond dit 0 tableaux de longueur sont légales en C99, ils sont en fait toujours pas légale en C99. Au lieu d'un tableau de longueur 0 ici, vous devriez utiliser un tableau de longueur 1

+1

"* C'est le cas car les tableaux de longueur nulle sont une entrée plus récente dans les standards. Normes C++ 11 interdit toujours les tableaux de longueur 0 (§8.3.4/1), ainsi que C99 (§6.7.5.2/1) – ildjarn

+0

@jildjarn je parsaisais essentiellement ce que Raymond a dit à la fin de son blog Je n'étais pas au courant que 0 tableaux de longueur étaient encore illégaux dans C99 jusqu'à une récente discussion de commentaire avec vous sur une autre question Je vais mettre à jour la réponse – JaredPar

+0

Désolé de nitpick une vieille réponse.: -PI seulement demander parce qu'une autre question lié ici comme "preuve" que les tableaux de longueur 0 étaient légaux C++.: -] – ildjarn

8

Votre intension sur "pourquoi ne pas utiliser un tableau de taille 1" est parfaite.

Le code effectue le "C struct hack" incorrect, car les déclarations de tableaux de longueur nulle sont une violation de contrainte. Cela signifie qu'un compilateur peut rejeter votre piratage dès la compilation au moment de la compilation avec un message de diagnostic qui arrête la traduction.

Si nous voulons perpétrer un hack, nous devons le faufiler devant le compilateur.

La bonne façon de faire le « C hack struct » (qui est compatible avec les dialectes C vont revenir à 1989 ANSI C, et probablement beaucoup plus tôt) est d'utiliser un tableau parfaitement valable de la taille 1:

struct someData 
{ 
    int nData; 
    unsigned char byData[1]; 
} 

de plus, au lieu de sizeof struct someData, la taille de la pièce avant byData est calculée à l'aide:

offsetof(struct someData, byData); 

allouer un struct someData avec un espace pour 42 octets dans byData, nous pourrions alors utiliser:

struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42); 

Notez que ce calcul offsetof est en fait le calcul correct même dans le cas de la taille du tableau étant égal à zéro. Vous voyez, sizeof la structure entière peut inclure le remplissage. Par exemple, si nous avons quelque chose comme ceci:

struct hack { 
    unsigned long ul; 
    char c; 
    char foo[0]; /* assuming our compiler accepts this nonsense */ 
}; 

La taille de struct hack est tout à fait peut-être rembourré pour l'alignement en raison de l'organe ul. Si unsigned long est quatre octets de large, alors très probablement sizeof (struct hack) est 8, alors que offsetof(struct hack, foo) est presque certainement 5. La méthode offsetof est la façon d'obtenir la taille précise de la partie précédente de la structure juste avant le tableau.

Donc, ce serait le moyen de refactoriser le code: le rendre conforme à la structure classique et hautement portable.

Pourquoi ne pas utiliser un pointeur? Parce qu'un pointeur occupe un espace supplémentaire et doit être initialisé.

Il existe d'autres bonnes raisons de ne pas utiliser un pointeur, à savoir qu'un pointeur nécessite un espace d'adressage pour être significatif. La struct hack est extériorisable: c'est-à-dire qu'il existe des situations dans lesquelles une telle mise en page est conforme au stockage externe tel que des zones de fichiers, paquets ou mémoire partagée, dans lesquelles vous ne voulez pas de pointeurs.

Il y a plusieurs années, j'ai utilisé le hack struct dans une interface de transmission de message de mémoire partagée entre le noyau et l'espace utilisateur. Je ne voulais pas de pointeurs là-bas, car ils n'auraient eu de sens que pour l'espace d'adressage d'origine du processus générant un message. La partie noyau du logiciel avait une vue sur la mémoire en utilisant son propre mapping à une adresse différente, et tout était basé sur des calculs de décalage.

+0

"qui est compatible avec les dialectes C depuis 1989" - l'accès au premier élément du tableau provoque un comportement indéfini même en C89. Le hack de structure repose sur le compilateur "définissant" ce comportement pour lui-même. –

0

Il vaut la peine de souligner IMO la meilleure façon de faire le calcul de la taille, qui est utilisé dans l'article de Raymond Chen lié ci-dessus.

struct foo 
{ 
    size_t count; 
    int data[1]; 
} 

size_t foo_size_from_count(size_t count) 
{ 
    return offsetof(foo, data[count]); 
} 

Le décalage de la première entrée à la fin de l'allocation souhaitée correspond également à la taille de l'allocation souhaitée. IMO c'est une façon extrêmement élégante de faire le calcul de la taille. Peu importe le type d'élément du tableau de taille variable. Le offsetof (ou FIELD_OFFSET ou UFIELD_OFFSET dans Windows) est toujours écrit de la même manière. Aucune expression sizeof() pour gâcher accidentellement.

Questions connexes