Pourquoi cette structure est-elle de taille 3 au lieu de 2?

J’ai défini cette structure:

typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col; 

Le sizeof(col) me donne la sortie de 3, mais ne devrait-il pas être 2? Si je ne commente qu’un seul élément, la sizeof est 2. Je ne comprends pas pourquoi: cinq éléments de 3 bits sont égaux à 15 bits, soit moins de 2 octets.

Existe-t-il une “taille interne” dans la définition d’une structure comme celle-ci? J’ai juste besoin d’un éclaircissement, car de ma conception du langage jusqu’à présent, je m’attendais à une taille de 2 octets, pas de 3.

Étant donné que vous utilisez char comme type sous-jacent pour vos champs, le compilateur essaie de regrouper les bits par octets et, comme il ne peut pas placer plus de huit bits dans chaque octet, il ne peut stocker que deux champs par octet.

La sum totale de bits utilisée par votre structure est de 15, donc la taille idéale pour adapter autant de données serait un short .

 #include  typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col; typedef struct { short A:3; short B:3; short C:3; short D:3; short E:3; } col2; int main(){ printf("size of col: %lu\n", sizeof(col)); printf("size of col2: %lu\n", sizeof(col2)); } 

Le code ci-dessus (pour une plate-forme 64 bits comme la mienne) donnera en effet 2 pour la deuxième structure. Pour tout ce qui est plus grand qu’un short , la structure ne remplira pas plus d’un élément du type utilisé, donc – pour cette même plate-forme – la structure se retrouvera avec la taille quatre pour int , huit pour long , etc.

Parce que vous ne pouvez pas avoir un champ de paquet de bits qui traverse la limite d’alignement minimum (qui est de 1 octet), ils vont probablement être emballés comme

 byte 1 A : 3 B : 3 padding : 2 byte 2 C : 3 D : 3 padding : 2 byte 3 E : 3 padding : 5 

(les ordres de champ / padding à l’intérieur du même octet ne sont pas intentionnels, c’est juste pour vous donner une idée, puisque le compilateur pourrait les définir comme il le souhaite)

Les deux premiers champs de bits correspondent à un seul caractère. Le troisième ne peut pas entrer dans ce personnage et en a besoin d’un nouveau. 3 + 3 + 3 = 9 qui ne rentre pas dans un caractère de 8 bits.

Ainsi, la première paire prend un caractère, la deuxième paire prend un caractère et le dernier champ de bit reçoit un troisième caractère.

La plupart des compilateurs vous permettent de contrôler le remplissage, par exemple en utilisant #pragma s . Voici un exemple avec GCC 4.8.1:

 #include  typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col; #pragma pack(push, 1) typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col2; #pragma pack(pop) int main(){ printf("size of col: %lu\n", sizeof(col)); // 3 printf("size of col2: %lu\n", sizeof(col2)); // 2 } 

Notez que le comportement par défaut du compilateur est là pour une raison et vous donnera probablement de meilleures performances.

Même si la norme ANSI C spécifie trop peu la façon dont les champs de bits sont compressés pour offrir un avantage significatif par rapport aux “compilateurs”, ils interdisent néanmoins aux compilateurs de comstackr les choses de la manière la plus efficace.

En particulier, si une structure contient des champs de bits, un compilateur est requirejs pour la stocker en tant que structure contenant un ou plusieurs champs anonymes d’un type de stockage “normal”, puis subdiviser logiquement chacun de ces champs en ses parties de bits. Ainsi, donné:

 unsigned char foo1: 3; unsigned char foo2: 3; unsigned char foo3: 3; unsigned char foo4: 3; unsigned char foo5: 3; unsigned char foo6: 3; unsigned char foo7: 3; 

Si le caractère unsigned char est de 8 bits, le compilateur devra allouer quatre champs de ce type et assigner deux champs de bits à tous les champs sauf un (ce qui correspond à un champ de caractères propre). Si toutes les déclarations de caractères avaient été remplacées par des short , il y aurait alors deux champs de type short , dont l’un contiendrait cinq champs de bits et l’autre contiendrait les deux autres.

Sur un processeur sans ressortingctions d’alignement, les données peuvent être disposées plus efficacement en utilisant unsigned short pour les cinq premiers champs et des caractères unsigned char pour les deux derniers, en stockant sept champs de trois bits sur trois octets. Bien qu’il soit possible de stocker huit champs de trois bits dans trois octets, un compilateur ne pourrait le permettre que s’il existait un type numérique à trois octets pouvant être utilisé comme type de “champ externe”.

Personnellement, je considère que les champs de bits tels que définis sont fondamentalement inutiles. Si le code doit fonctionner avec des données empaquetées en binary, il doit explicitement définir les emplacements de stockage des types réels, puis utiliser des macros ou d’autres moyens de ce type pour accéder aux bits correspondants. Il serait utile que C supporte une syntaxe comme:

 unsigned short f1; unsigned char f2; union foo1 = f1:0.3; union foo2 = f1:3.3; union foo3 = f1:6.3; union foo4 = f1:9.3; union foo5 = f1:12.3; union foo6 = f2:0.3; union foo7 = f2:3.3; 

Une telle syntaxe, si elle est autorisée, permettrait au code d’utiliser des champs de bits de manière portable, sans tenir compte des tailles de mots ou des ordres d’octets (foo0 serait dans les trois bits les moins significatifs de f1, mais ceux-ci pourraient être stockés au adresse inférieure ou supérieure). En l’absence d’une telle fonctionnalité, cependant, les macros sont probablement le seul moyen portable de fonctionner avec de telles choses.