Les variables de champ de bits C impriment des valeurs inattendues

struct m { int parent:3; int child:3; int mother:2; }; void main() { struct m son={2,-6,5}; printf("%d %d %d",son.parent,son.child,son.mother); } 

Quelqu’un peut-il s’il vous plaît aider à dire pourquoi la sortie du programme est 2 2 1 ?

Suppression de tous les bits sauf les bits significatifs pour les champs indiqués:

 parent: 3 bits (1-sign bit + 2 more), value 010, result 2 child: 3 bits (1-sign bit + 2 more), value 010, result 2 mother: 2 bits (1 sign bit + 1 more), value 01, result 1 

Détails

Il convient de noter que vos champs de structure sont déclarés comme des valeurs de champs de bits int . En C99-§6.7.2,2, les types suivants sont tous équivalents: int , signed ou signed int . Par conséquent, vos champs de structure sont signés . En C99-§6.2.6.2,2, un de vos bits doit être utilisé pour représenter le “signe” de la variable (négatif ou positif). En outre, la même section indique que si l’on exclut le bit de signe, la représentation des bits restants doit correspondre à un type associé non signé du compte de bits restants. C99-§6.7.2,1 définit clairement comment chacun de ces bits représente une puissance de 2. Par conséquent, le seul bit normalement utilisé en tant que bit de signe est le bit le plus significatif (c’est le seul qui rest, mais Je suis tout à fait sûr que s’il s’agit d’une interprétation inexacte de la norme, j’en entendrai parler en temps voulu). Le fait que vous affectiez un nombre négatif dans l’une des valeurs de test utilisées pour votre échantillon suggère que vous en êtes peut- être conscient, mais que de nombreuses personnes récemment exposées à des champs de bits ne le sont pas. Ainsi, il convient de noter.

Les sections suivantes de la norme C99 sont référencées dans le rest de cette réponse. Le premier traite des promotions vers différents types, le suivant l’évaluation et le changement de valeur potentiel (le cas échéant). La dernière est importante pour comprendre comment un type int type bit-fields est déterminé.

C99-§6.3.1.1: Booléen, caractères et entiers

2: si un int peut représenter toutes les valeurs du type d’origine (comme le limite la largeur, pour un champ de bits), la valeur est convertie en un int ; sinon, il est converti en un unsigned int . Celles-ci sont appelées promotions entières. Tous les autres types ne sont pas modifiés par les promotions entières.

C99-§6.3.1.3 Entiers signés et non signés

  1. Lorsqu’une valeur de type entier est convertie en un type entier autre que _Bool, si la valeur peut être représentée par le nouveau type, elle est inchangée.
  2. Sinon, si le nouveau type n’est pas signé, la valeur est convertie en ajoutant ou en soustrayant plusieurs fois la valeur maximale pouvant être représentée dans le nouveau type jusqu’à ce que la valeur se situe dans la plage du nouveau type.
  3. Sinon, le nouveau type est signé et la valeur ne peut pas y être représentée. soit le résultat est défini par l’implémentation, soit un signal défini par l’implémentation est généré.

C99-§6.7.2.1 Spécificateurs de structure et d’union

10: un champ de bits est interprété comme ayant un type entier signé ou non signé composé du nombre de bits spécifié. Si la valeur 0 ou 1 est stockée dans un champ de bits de largeur différente de zéro, de type _Bool, la valeur du champ de bits doit être égale à la valeur stockée. un champ de bits _Bool a la sémantique d’un _Bool.

Considérez la représentation régulière des bits int de vos valeurs de test. Ce qui suit concerne une implémentation int 32 bits:

 value : s bits 2 : 0 0000000 00000000 00000000 00000010 <== note bottom three bits -6 : 1 1111111 11111111 11111111 11111010 <== note bottom three bits 5 : 0 0000000 00000000 00000000 00000101 <== note bottom two bits 

Parcourez chacun de ces éléments en appliquant les exigences des références standard ci-dessus.

int parent:3 : le premier champ est un entier signé de 3 bits, auquel la valeur décimale 2 est atsortingbuée. Le type rvalue, int , englobe-t-il le type lvalue, int:3 ? Oui, alors les types sont bons. La valeur 2 correspond-elle à la plage du type lvalue? Eh bien, 2 peut facilement tenir dans un int:3 , donc aucun déchiquetage de valeur n’est requirejs. Le premier champ fonctionne bien.

int child:3 : le deuxième champ est également un entier signé de 3 bits, cette fois-ci étant affecté à la valeur décimale -6 . Encore une fois, le type rvalue ( int ) englobe-t-il complètement le type lvalue ( int:3 )? Oui, encore une fois, les types vont bien. Toutefois, le nombre minimal de bits requirejs pour représenter -6 , une valeur signée, est de 4 bits. ( 1010 ), qui représente le bit le plus significatif en tant que bit de signe. Par conséquent, la valeur -6 est en dehors de la plage de stockage autorisée d'un champ de bits signé de 3 bits. Par conséquent, le résultat est défini par la mise en œuvre conformément au § 6.3.1.3-3.

int mother:2 Le dernier champ est un entier signé de 2 bits, cette fois la valeur décimale étant atsortingbuée à 5. De nouveau, le type rvalue ( int ) englobe-t-il complètement le type lvalue ( int:2 )? Oui, encore une fois, les types vont bien. Cependant, encore une fois, nous sums confrontés à une valeur qui ne peut pas correspondre au type de cible. Le nombre minimal de bits requirejs pour représenter un signe positif signé 5 est quatre: (0101). Nous n'avons que deux avec qui travailler. Par conséquent, le résultat est à nouveau défini par la mise en œuvre conformément au § 6.3.1.3-3.

Par conséquent, si je prends cela correctement, la mise en œuvre dans ce cas élimine simplement tous les bits sauf les bits requirejs nécessaires pour stocker la profondeur de bits déclarée. Et les résultats de ce piratage sont ce que vous avez maintenant. 2 2 1

Remarque

Il est tout à fait possible que j'ai inversé l'ordre de la promotion de manière incorrecte (il m'est facile de me perdre dans la norme, car je suis dyslexique et je retourne des choses dans ma tête périodiquement). Si tel est le cas, je demanderais à toute personne ayant une interprétation plus ssortingcte de la norme de bien vouloir m'en faire part et de répondre en conséquence.

La taille des champs de bits pour l’ child et la mother est trop petite pour contenir les valeurs constantes que vous leur affectez et elle déborde.

Vous pouvez remarquer qu’en phase de compilation, vous recevrez les avertissements suivants:

 test.c: In function 'main': test.c:18:11: warning: overflow in implicit constant conversion test.c:18:11: warning: overflow in implicit constant conversion 

C’est parce que vous avez défini vos variables comme 3 bits d’un int et non l’int entier. Over flow signifie qu’il est impossible de stocker vos valeurs dans une mémoire de 3 bits.

si vous utilisez la définition de structure suivante, vous éviterez l’avertissement de la compilation et obtiendrez les bonnes valeurs:

 struct m { int parent; int child; int mother; }; 

Vous ne pouvez pas représenter -6 en seulement 3 bits; De même, vous ne pouvez pas représenter 5 en seulement deux bits.

Y a-t-il une raison pour laquelle le parent , l’ child et la mother doivent être des champs de bits?

child besoin d’au moins 4 bits pour contenir -6 ( 1010 ). Et la mother besoin d’au moins 4 bits pour tenir 5 ( 0101 ).

printf ne considère que les 3 derniers bits de l’ child donc son impression 2 et il ne considère que les 2 derniers bits de la mother ainsi son impression 1 .

Vous pensez peut-être que l’ child n’a besoin que de 3 bits pour stocker -6 , mais il nécessite en fait 4 bits, y compris le bit de signe. Les valeurs négatives doivent être stockées en mode complément à 2.

 Binary equivalent of 6 is 110 one`s complement of 6 is 001 two`s complement of 6 is 010 sign bit should be added at MSB. 

Donc, la valeur de -6 est 1010 . printf omet le bit de signe.

Le premier de tous:

5 est un 101 en binary, donc il ne tient pas dans 2 bits.

-6 également ne rentre pas dans 3 bits.

Vos champs de bits sont trop petits. Aussi: s’agit-il simplement d’un exercice ou essayez-vous d’optimiser (prématurément)? Tu ne vas pas être très content du résultat … ça va être assez lent.

J’ai finalement trouvé la raison après avoir appliqué la pensée 🙂

2 est représenté en binary par 0010

-6 à 1010

et cinq comme 0101

Maintenant, 2 peut être représenté en utilisant seulement 3 bits afin qu’il soit stocké comme 010

-6 serait stocké dans 3 bits comme 010

et cinq seraient stockés dans 2 bits comme 01

Donc, le résultat final serait 2 2 1

Merci à tous pour votre réponse !!!!