1
votes

Comportement inattendu utilisant des champs de bits et des unions

J'expérimentais avec des champs de bits et des unions et j'ai créé ceci:

union REG{
    struct{
        char posX: 7;
        char posY: 7;
        unsigned char dir:  2;
    };

    unsigned short reg;
};

Et quand j'exécute sizeof (short) , j'obtiens 2, mais quand je lancez sizeof (REG) , j'obtiens 4. C'est bizarre pour moi parce que quand je additionne les bits, j'obtiens 7 + 7 + 2 = 16, qui est la taille en bits d'un type de données à 2 octets.

J'utilise actuellement l'éditeur Dev-C ++ avec le compilateur TDM-GCC 9.9.2 Débogage 64 bits .

Ceci est ma première question, donc s'il vous plaît dites-moi si vous avez besoin de plus d'informations ... Merci d'avance!

Modifier: Après d'autres expériences, j'ai réalisé que la taille est la même (2 bytes) lorsque je règle la taille de posX et posY sur 6 bits. Mais cela laisse encore perplexe car la somme est de 14 bits, ce qui fait moins de 2 octets ...

Edit 2: Grâce à AviBerger, j'ai réalisé que le remplacement du type de données char / unsigned char par short / unsigned short le résultat de '' 'sizeof (REG)' '' devient 2. Mais je n'arrive toujours pas à comprendre "Pourquoi cela se produit-il?"

p >


7 commentaires

Par curiosité, que se passe-t-il si vous utilisez short comme type de bitfield?


Voir: sur certaines plates-formes, les champs de bits ne chevauchent pas les octets, sur d'autres ils le font


@AviBerger Cela fonctionnait en fait en utilisant le type de données court, mais je ne comprends toujours pas pourquoi, je suis sur le point de lire le lien que vous avez partagé


J'ai bidouillé un peu sur coliru . Je suppose que vous ne pouvez pas emballer 7 + 7 + 2 bits dans char ou unsigned char (vous ne pouvez pas non plus 7 + 7 ou 7 + 2). Par conséquent, les bits ne sont pas serrés. Ceci est différent avec unsigned short qui fournit au moins 16 bits (par standard).


Le meilleur à ce sujet, j'ai trouvé dans cppreference : Si le la taille du champ de bits est supérieure à la taille de son type, la valeur est limitée par le type: a std :: uint8_t b: 1000; contiendrait toujours des valeurs comprises entre 0 et 255. les bits supplémentaires deviennent un remplissage inutilisé. et Plusieurs champs de bits adjacents sont généralement regroupés (bien que ce comportement soit défini par l'implémentation) ;-)


@Scheff ouais, lisez également cela, mais je ne pense pas que cela aide ici car chaque champ de bits est attribué à une taille d'au plus 7 bits, ce qui est inférieur à un octet (taille du caractère), alors j'ai pensé que cela ne devrait pas causer des problèmes, clairement j'avais tort, comme on le voit dans edit 2 ...


Il semble que l'implémentation que vous utilisez ne chevauchera pas un champ de bits sur 2 caractères différents lorsque char est le type déclaré. Comme Scheff l'a laissé entendre, il place un champ 7 dans le premier caractère, un champ 7 dans le deuxième caractère, un champ 2 dans le troisième (et ajoute un quatrième pour le remplissage à des fins d'alignement). Si vos tailles de champ étaient de 5, 3 et 7, je prédis que ce serait 2 octets car les 5 et 3 tiendraient dans un seul octet ne traversant pas une limite de caractères. Lorsque vous passez au short, tout tient dans un seul short. Cependant, comme @Scheff l'a indiqué, il s'agit d'une implémentation définie. Différents compilateurs pourraient le faire différemment.


3 Réponses :


1
votes

Il y a plusieurs points de finesse lorsque l'on travaille avec struct et union . Le plus courant est que les champs sont généreusement remplis pour être alignés sur la taille des mots du processeur.

#pragma pack(1)   // this particular syntax is valid for many compilers

struct {
     char  c11;
     char  c12;
} s2;

#pragma pack(4)

semble que cela devrait être une structure à deux octets, mais étonnamment souvent sizeof (s1) ne sera pas 2, mais 4 - ou même 8. C'était le cas même dans les années 1980 avec des machines 16 bits.

C'est parce que les compilateurs C et C ++ aligneront chaque caractère élément d'une structure à une limite de deux ou quatre octets. Je n'ai pas encore vu les éléments de structure alignés sur une limite de 8 octets, mais nous n'avons pas encore d'architecture 64 bits si nécessaire.

La solution est d'invoquer une option de compilation pour "pack structures". Cela peut être fait sur la ligne de commande du compilateur ou en incluant une option #pragma appropriée avant la déclaration de structure:

struct {
     char   c1;
     char   c2;
} s1;


0 commentaires

1
votes

Tiré de la norme (n4835):

11.4.9 Champs de bits [class.bit]
1 [...] L'allocation de champs de bits dans un objet de classe est définie par l'implémentation. L'alignement des champs de bits est défini par l'implémentation. Les champs de bits sont regroupés dans une unité d'allocation adressable. [Remarque: les champs de bits chevauchent les unités d'allocation sur certaines machines et pas sur d'autres. Les champs de bits sont attribués de droite à gauche sur certaines machines, de gauche à droite sur d'autres. —End note]

Comme vous le voyez, la taille et l'alignement sont définis par l'implémentation. Vous pouvez donc obtenir le comportement attendu sur d'autres compilateurs / plates-formes, mais sur votre compilateur / plate-forme, vous obtenez des résultats différents de ceux que vous attendez.


0 commentaires

2
votes

D'après les spécifications que nous avons

Une implémentation peut allouer n'importe quelle unité de stockage adressable suffisamment grande pour contenir un bit- domaine. S'il reste suffisamment d'espace, un champ de bits qui suit immédiatement un autre champ de bits dans un la structure doit être emballée dans des bits adjacents de la même unité. S'il reste un espace insuffisant, si un champ de bits qui ne rentre pas est placé dans l'unité suivante ou chevauche des unités adjacentes est défini par la mise en œuvre. L'ordre d'attribution des champs de bits au sein d'une unité (d'ordre supérieur à d'ordre inférieur ou d'ordre inférieur à d'ordre élevé) est défini par l'implémentation. L'alignement du l'unité de stockage adressable n'est pas spécifiée.

Le comportement réel dépend donc de l'unité d'allocation de taille choisie par le compilateur pour les champs de bits, et s'il permet aux champs de s'étendre sur plusieurs unités d'allocation. Ce choix est défini par l'implémentation, mais une implémentation courante consiste à utiliser le type déclaré du champ de bits comme unité d'allocation, et à ne pas autoriser le franchissement des limites d'unité d'allocation. Ainsi, lorsque vous utilisez (unisgned) char, il utilise une unité d'allocation de 8 bits. Cela signifie qu'aucun des deux champs de bits ne peut être combiné en un seul octet (7 + 7> 8 et 7 + 2> 8), il finit donc par prendre 3 unités d'allocation (octets), qui arrondissent ensuite à 4 pour l'alignement lorsque combiné avec un court dans l'union.

Lorsque vous modifiez la taille du champ de bits à 6, les deuxième et troisième champs de bits peuvent désormais tenir dans un octet (6 + 2 = 8), donc cela ne prend que deux unités d'allocation.

Lorsque vous modifiez le type de champ de bits en short , il utilise une unité d'allocation de 16 bits, de sorte que les 3 champs de bits peuvent tenir dans une unité d'allocation.


0 commentaires