Représentation du champ de bits signé

J’ai créé un champ de bits avec un champ de 1 bit et ai utilisé int au lieu de unsigned . Plus tard, quand j’ai essayé de vérifier la valeur du champ, j’ai trouvé que la valeur était -1. J’ai utilisé ce code pour vérifier la représentation binary et la valeur de mon champ de bits:

 #include  #include  union { struct { int bit:1; } field; int rep; } n; int main() { int c, k; n.field.bit=1; for (c = 31; c >= 0; c--) { k = n.rep >> c; if (k & 1) printf("1"); else printf("0"); } printf("\n %d \n", n.field.bit); return 0; } 

la sortie était: 00000000000000000000000000000001

-1

Dans ce cas, pourquoi la valeur de mon champ de bits est-elle -1 et s’agit-il toujours d’un nombre négatif lorsque j’utilise signed int au lieu de unsigned?

N’utilisez jamais plain int tant que type de champ binary si vous attendez quelque chose à propos de la valeur, à part qu’il puisse contenir n bits. Selon la norme C11, il est en fait défini par la mise en oeuvre, que int soit dans un champ binary signé ou 6.7.2p5 non signé :

5 Chacun des multisets séparés par des virgules désigne le même type, sauf que pour les champs de bits, il est défini par l’implémentation, que le spécificateur int désigne le même type que signed int ou le même type que unsigned int .

Dans votre cas, l’ int désigne le même type que signed int ; c’est le défaut dans GCC :

Indique si un champ de bits «simple» est traité comme un champ de bits signé ou comme un champ de bits non signé (C90 6.5.2, C90 6.5.2.1, C99 et C11 6.7.2, C99 et C11 6.7.2.1). ).

Par défaut, il est traité comme un -funsigned-bitfields signed int mais cela peut être modifié par l’option -funsigned-bitfields .

Ensuite, il est défini par l’implémentation si les nombres signés sont dans son complément ou dans un complément à deux – s’ils sont dans son complément, la seule valeur pouvant être stockée dans 1 bit est le bit de signe, donc 0; le champ de bit ainsi signé d’un bit n’a pas de sens sur son complément. Cependant, votre système utilise un complément à 2 – il s’agit par exemple de ce que GCC utilise toujours :

Si les types entiers signés sont représentés en utilisant signe et magnitude, complément à deux ou complément à un, et si la valeur extraordinaire est une représentation de piège ou une valeur ordinaire (C99 et C11 6.2.6.2).

GCC ne prend en charge que les types entiers du complément à deux et tous les modèles de bits sont des valeurs ordinaires.

et ainsi les valeurs de bit 1 et 0 sont interprétées en termes de nombres de complément à deux signés: le premier a un bit de signe défini, donc il est négatif ( -1 ) et le dernier n’a pas de bit de signe défini, il n’est donc pas négatif ( 0 ).

Ainsi, pour un champ binary signé de 2 bits, les modèles de bits possibles et leurs valeurs entières sur une machine à complément à 2 sont:

  • 00 – a une valeur int 0
  • 01 – a int valeur 1
  • 10 – a int valeur -2
  • 11 – a une valeur int -1

Dans un champ de n bits, le nombre minimal de signés est – 2 ^ (n – 1) et le maximum est 2 ^ (n-1) – 1.

À présent, lorsque l’arithmétique est effectuée sur un opérande entier signé dont le rang est inférieur à int , il est d’abord converti en int , et la valeur -1 correspond donc à l’extension de signe à la largeur entière int ; il en va de même pour les promotions d’arguments par défaut ; la valeur est étendue du signe à un (largeur entière) int lorsqu’elle est transmise à printf .

Ainsi, si vous attendez une valeur sensible d’un champ binary, utilisez l’un unsigned bit: 1; ou bien si cela doit être compris comme un drapeau booléen, _Bool bit: 1;

Lorsque vous appelez une fonction d’argument variadique (comme printf ), certains arguments sont promus . Par exemple, les champs de bits subissent une promotion entière où ils sont promus à une valeur int ordinaire. Cette promotion comporte une extension de signature (car votre type de base pour le champ binary est signé). Cette extension de signe fera -1 .

Lorsque vous utilisez des champs de bits, utilisez toujours des types non signés comme base.