L’ordre des membres dans une structure est-il important?

J’ai trouvé un comportement particulier en C. Considérez le code ci-dessous:

struct s { int a; }; struct z { int a; struct sb[]; }; int main(void) { return 0; } 

Il comstack très bien. Puis changez l’ordre des membres de struct z comme ça

 struct z { struct sb[]; int a; }; 

Et tout à coup, le field has incomplete type 'struct s []' erreur de compilation field has incomplete type 'struct s []' .

Pourquoi donc?

L’ordre des champs dans une struct est important – le compilateur n’est pas autorisé à réorganiser les champs, de sorte que la taille de la struct peut changer à la suite de l’ajout de remplissage.

Dans ce cas, cependant, vous définissez un membre dit flexible , un tableau dont vous pouvez modifier la taille. Les règles pour les membres flexibles sont que

  • Il ne peut jamais y en avoir plus d’un,
  • Si présent, le membre flexible doit être le dernier de la struct et
  • La struct doit avoir au moins un membre en plus du membre flexible.

Jetez un coup d’oeil à cette Q & R pour une petite illustration sur l’utilisation des membres de la structure flexible.

Le compilateur ne peut pas calculer combien de mémoire struct sb[]; va consumr Cela signifie que si la structure contient des champs, le compilateur ne peut pas déterminer où se trouvent ces champs.

C’était (dans les anciennes versions de C) que (par exemple) struct sb[]; n’était pas autorisé en tant que membre d’une structure. Cela a rendu la gestion efficace de la mémoire agaçante. Pour un exemple simple, imaginez que vous avez une structure contenant une chaîne “name” (qui peut ne contenir que quelques caractères ou beaucoup). Vous pouvez utiliser un tableau de taille fixe suffisamment grand pour le plus grand nom (qui gaspille de l’espace), ou utiliser un pointeur et allouer 2 morceaux de mémoire (un pour la structure et un pour la chaîne de nom de longueur variable). Vous pouvez également utiliser un pointeur et indiquer un espace supplémentaire au-delà de la fin de la structure, ce qui aboutit à quelque chose comme ceci:

  length = strlen(my_ssortingng); foo = malloc(sizeof(MYSTRUCTURE) + length + 1); foo->name = (void *)foo + sizeof(MYSTRUCTURE); // Set pointer to extra bytes past end of structure memcpy(foo->name, my_ssortingng, length + 1); 

C’était l’option la plus efficace. mais c’est aussi moche et sujet aux erreurs.

Pour contourner ce problème, les compilateurs ont ajouté des extensions non standard pour autoriser les “tableaux de taille inconnue” à la fin de la structure. Cela l’a rendu un peu plus facile pour les programmeurs et l’a rendu un peu plus efficace (car il n’y a pas besoin du membre de pointeur supplémentaire). Cela a fini par être adopté par la norme C (peut-être en C99 – je ne me souviens pas).

L’ordre des membres est généralement important (c.-à-d. Qu’un remplissage peut être inséré entre les champs), mais dans votre cas particulier, vous utilisez un tableau de membres flexible , ce qui est standardisé dans C99 – 6.7.2.1.16.

En tant que cas particulier, le dernier élément d’une structure avec plus d’un membre nommé peut avoir un type de tableau incomplet; cela s’appelle un membre de tableau flexible. Dans la plupart des cas, le membre de tableau flexible est ignoré. En particulier, la taille de la structure est comme si le membre de tableau flexible était omis, sauf qu’il pouvait avoir plus de remplissage que l’omission impliquerait.

Votre struct sb[]; member est destiné à être utilisé pour accéder aux allocations de tas dynamics pour plusieurs éléments de struct s .

Le titre de votre question est “L’ordre des membres dans une struct important?”.

Le problème évident dans votre code est lié au fait que votre struct contient un membre flexible.

Voici donc un problème supplémentaire lié à la question générale de l’ordre des membres dans une struct :


Prenez les deux structures suivantes par exemple:

 struct s1 { int a; short b; char c; }; struct s2 { char c; short b; int a; }; 

La plupart des compilateurs appendont un remplissage pour aligner chaque membre sur une adresse divisible par sa taille.

Donc, struct s2 peut finalement comstackr en:

 struct s2 { char c; char pad1; short b; short pad2; short pad3; int a; }; 

Cela aboutira éventuellement à différentes tailles pour les instances des types struct s1 et struct s2 .

La commande est importante DANS CE CAS. Votre struct z contient un tableau composé de structs s . Cependant, ce tableau n’a pas de taille associée, de sorte que le compilateur ne sait pas comment allouer l’espace de stack approprié, car il existe un autre champ de la structure ( int a ) par la suite. Un exemple de la façon dont cela fonctionnerait:

 struct s { int a; } struct z { struct sb[10]; int a; } int main(void) { return 0; } 

Si vous avez vraiment besoin du tableau pour changer la taille, il est préférable d’allouer la structure entière sur le tas avec le tableau comme pointeur vers les struct s , puis de le réallouer dynamicment pour s’adapter à la taille changeante du tableau. Recherchez malloc (3) , realloc 3) , calloc (3) et free (3) .