J'ai vu le code ci-dessous dans ce post Quora :
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
struct mystruct s;
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}
En C & C++, la sortie du code est inattendue ,
Est désactivé !!
Bien que l'explication relative au "bit de signe" soit donnée dans cet article, je suis incapable de comprendre comment il est possible que nous fixions quelque chose et que cela ne reflète pas ce qu'il est.
Quelqu'un peut-il donner une explication plus élaborée?
Remarque : Les deux balises c & c ++ sont obligatoires, car leurs normes diffèrent légèrement par leur description. les champs de bits. Voir les réponses pour spécification C et spécification C++ .
Conformément à norme C++ n471 , un extrait de code très similaire est fourni. Le type utilisé est BOOL
(personnalisé), mais il peut s'appliquer à tout type.
12.2.4
4 Si la valeur true ou false est stockée dans un champ de bits de type
bool
de taille quelconque (y compris un champ de bits à un bit), la valeur initialebool
et la valeur du champ de bits doivent comparer égaux. Si la valeur d'un énumérateur est stockée dans un champ de bits du même type d'énumération et que le nombre de bits dans le champ de bits est suffisamment grand pour contenir toutes les valeurs de ce type d'énumération (10.2), l'original la valeur de l'énumérateur et la valeur du champ de bits doivent être comparées égales]. [ Exemple:enum BOOL { FALSE=0, TRUE=1 }; struct A { BOOL b:1; }; A a; void f() { a.b = TRUE; if (a.b == TRUE) // yields true { /* ... */ } }
- fin exemple]
Au premier regard, la partie audacieuse semble ouverte à l'interprétation. Cependant, l'intention correcte devient claire lorsque le enum BOOL
est dérivé de la int
.
enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
struct mystruct s;
s.enabled = TRUE;
if(s.enabled == TRUE)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}
Avec le code ci-dessus, il donne un avertissement sans -Wall -pedantic
:
avertissement: "mystruct :: enabled" est trop petit pour contenir toutes les valeurs de "enum BOOL"
struct mystruct { BOOL enabled:1; };
La sortie est:
Est désactivé !! (en utilisant
enum BOOL : int
)
Si enum BOOL : int
est simplifié enum BOOL
, alors le résultat obtenu est tel que spécifié ci-dessus:
Est activé (en utilisant
enum BOOL
)
Par conséquent, on peut en conclure, comme peu d’autres réponses l’ont fait, que le type int
n’est pas assez grand pour stocker la valeur "1" dans un seul champ de bits.
Les champs de bits sont incroyablement mal définis par la norme. Étant donné ce code struct mystruct {int enabled:1;};
, alors nous ne savons pas savons:
int:n
doit être considéré comme signé ou non signé.En ce qui concerne la dernière partie, C17 6.7.2.1/10 dit:
Un champ de bits est interprété comme ayant un type entier signé ou non signé composé du nombre de bits spécifié 125)
Note non normative expliquant ce qui précède:
125) Comme spécifié au 6.7.2 ci-dessus, si le spécificateur de type utilisé est
int
ou un nom de type défini commeint
, il est défini par l'implémentation que le champ de bits soit signé ou non.
Si le champ binaire doit être considéré comme signed int
et que vous créez un peu de taille 1
, il n'y a plus de place pour les données, mais uniquement pour le bit de signe. C'est la raison pour laquelle votre programme peut donner des résultats étranges sur certains compilateurs.
Bonnes pratiques:
int
pour toute forme de manipulation de bits.Je suis incapable de comprendre, comment est-il possible que nous fixions quelque chose et que cela ne se présente pas tel quel.
Demandez-vous pourquoi il compile vs vous donne une erreur?
Oui, cela devrait idéalement vous donner une erreur. Et c'est le cas si vous utilisez les avertissements de votre compilateur. Dans GCC, avec -Werror -Wall -pedantic
:
main.cpp: In function 'int main()':
main.cpp:7:15: error: overflow in conversion from 'int' to 'signed char:1'
changes value from '1' to '-1' [-Werror=overflow]
s.enabled = 1;
^
La raison pour laquelle cela reste jusqu'à être défini par l'implémentation par opposition à une erreur peut avoir plus à voir avec les utilisations historiques, où exiger un transtypage signifierait briser l'ancien code. Les auteurs de la norme peuvent penser que les avertissements ont suffi à prendre le relais pour les personnes concernées.
Pour ajouter un peu de prescriptivisme, je ferai écho à la déclaration de @ Lundin: "N'utilisez jamais de champs de bits pour quelque raison que ce soit." Si vous avez le genre de bonnes raisons pour obtenir des informations de bas niveau et spécifiques sur votre mémoire des détails de mise en page qui vous feraient penser que vous aviez besoin de champs de bits en premier lieu, les autres exigences associées que vous avez presque certainement se heurteront à leur sous-spécification.
(TL; DR - Si vous êtes assez sophistiqué pour légitimement "avoir besoin" de champs de bits, ils ne sont pas assez bien définis pour vous servir.)
C'est le comportement défini par l'implémentation. Je pars du principe que les machines sur lesquelles vous utilisez ceci utilisent des entiers signés à deux compléments et traitent int
dans ce cas comme un entier signé pour expliquer pourquoi vous n'entrez pas si vrai dans l'instruction if.
struct mystruct { int enabled:1; };
déclare enable
sous la forme d'un champ de bits à 1 bit. Comme il est signé, les valeurs valides sont -1
et 0
. La définition du champ sur 1
déborde ce bit qui revient à -1
(il s'agit d'un comportement non défini).
Essentiellement, lorsqu'il s'agit d'un champ de bits signé, la valeur maximale est 2^(bits - 1) - 1
qui est 0
dans ce cas.
Vous pourriez penser que, dans le système complémentaire du complément à 2, le bit le plus à gauche est le bit de signe. Tout entier signé avec le bit le plus à gauche est donc une valeur négative.
Si vous avez un entier signé sur 1 bit, il n’a que le bit de signature. Ainsi, l’attribution de 1
à ce bit ne peut définir que le bit de signe. Ainsi, lors de la relecture, la valeur est interprétée comme négative, de même que -1.
Les valeurs qu'un entier signé de 1 bit peut contenir sont -2^(n-1)= -2^(1-1)= -2^0= -1
et 2^n-1= 2^1-1=0
Il n'y a rien de mal à ce que vous compreniez les champs de bits. Ce que je vois, c'est que vous avez d'abord redéfini mystruct en tant que struct mystruct {int enabled: 1;}, puis en tant que struct mystruct s;. Ce que vous auriez dû coder était:
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
mystruct s; <-- Get rid of "struct" type declaration
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}