Quel est le type d’un bitfield?

Je ne trouve nulle part dans la norme C où cela est spécifié. Par exemple, dans

struct { signed int x:1; } foo; 

foo.x est- foo.x une lvalue de type int ou autre chose? Il ne semble pas naturel que ce soit une lvalue de type int puisque vous ne pouvez y stocker aucune valeur de type int , seulement 0 ou -1, mais je ne trouve aucun langage qui lui atsortingbuerait un type différent. Bien sûr, utilisé dans la plupart des expressions, il serait promu à int toute façon, mais le type réel fait une différence en C11 avec _Generic , et je ne trouve pas de langage dans la norme sur la façon dont les champs de _Generic interagissent avec _Generic non plus.

Comme Jonathan l’a déjà mentionné, p5 indique clairement le type d’un champ de bits.

Ce que vous devriez également garder à l’esprit, c’est qu’il existe une règle spéciale pour les conversions arithmétiques en champs binarys dans 6.3.1.1, stipulant en principe que si un int peut représenter toutes les valeurs, un tel champ binary est converti en int dans la plupart des expressions.

Ce que le type serait dans un _Generic devrait être le type déclaré (modulo the glitch signe), car il semble y avoir un consensus sur le fait que les conversions arithmétiques ne s’appliquent pas, là. Alors

 _Generic((X), int: toto, unsigned: tutu) _Generic(+(X), int: toto, unsigned: tutu) 

pourrait vous donner des résultats différents si X est un champ de bits non signé avec une largeur dont toutes les valeurs tiennent dans un int .

Étant donné que vous avez inclus le qualificatif signed , les seules valeurs pouvant être stockées dans le champ de bit 1 bit sont bien -1 et 0. Si vous aviez omis le qualificatif, il serait défini par l’implémentation si le bit ‘simple’ int le champ était signé ou non signé. Si vous aviez spécifié unsigned int , bien sûr, les valeurs seraient 0 et +1.

Les sections pertinentes de la norme sont les suivantes:

§6.7.2.1 Spécificateurs de structure et d’union

¶4 L’expression qui spécifie la largeur d’un champ de bits doit être une expression constante entière avec une valeur non négative qui ne dépasse pas la largeur d’un object du type qui serait spécifié si les deux points et l’expression étaient omis. 122) Si la valeur est zéro, la déclaration ne doit pas avoir de déclarant.

¶5 Un champ de bits doit avoir un type qui est une version qualifiée ou non qualifiée de _Bool , un _Bool signed int , un unsigned int , ou un autre type défini par la mise en œuvre. L’implémentation est définie si les types atomiques sont autorisés.

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

122) Bien que le nombre de bits dans un object _Bool soit au moins CHAR_BIT à CHAR_BIT , la largeur (nombre de bits de signe et de valeur) d’un _Bool peut n’être que d’un bit.

125) Comme spécifié au 6.7.2 ci-dessus, si le spécificateur de type réel utilisé est int ou un nom de type au défaut défini comme int , il est alors défini par l’implémentation que le champ de bit soit signé ou non.

La note de bas de page 125 pointe vers:

§6.7.2 Spécificateurs de type

¶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 .

La spécification C11 ne le dit certainement pas et est peut-être déficiente.

Je crois que foo.x est une lvalue avec un type autre que int , mais ma justification est assez faible:

6.2.7 Le paragraphe 1 dit:

Deux types sont compatibles si leurs types sont identiques.

Le paragraphe 2 de l’article 6.3 dit:

La conversion d’une valeur d’opérande en un type compatible ne provoque aucune modification de la valeur ou de la représentation.

Si foo.x est une lvalue de type int , alors il serait compatible avec d’autres foo.x = 5 donc foo.x = 5 devrait avoir pour résultat foo.x ayant la valeur 5 (par 6.3p2). Cela ne peut évidemment pas arriver, suggérant que foo.x n’est pas compatible avec int , suggérant que foo.x n’est pas une lvalue de type int .

Cela n’a pas vraiment de sens que foo.x n’est pas compatible avec int . Peut-être qu’aucune conversion (au sens 6.3.1) ne se produit et que foo.x obtient sa valeur via un mécanisme non décrit dans la norme. Ou peut-être que je ne comprends pas ce que «opérandes arithmétiques» signifie, et que 6.3.1 ne s’applique pas à lvalues.

Il y a aussi le paragraphe 6.3.1.1, point 1, qui dit:

  • Le rang d’un type entier signé doit être supérieur au rang de tout type entier signé avec moins de précision.

foo.x a moins de précision qu’un int ordinaire (lorsqu’il est utilisé en tant que lvalue et non lorsqu’il est “converti en la valeur stockée dans l’object désigné” comme décrit au 6.3.2.1p2), il doit donc avoir un rang de conversion entier différent . Cela suggère également que ce n’est pas un int .

Mais je ne suis pas sûr que mon interprétation soit valable ou conforme à l’intention du comité.

Je recommanderais de soumettre un rapport de défaut à ce sujet.

La norme C11 stipule en 6.7.2.1p5:

Un champ de bits doit avoir un type qui est une version qualifiée ou non qualifiée de _Bool, un entier signé, un entier non signé, ou un autre type défini par l’implémentation.

Il s’agit d’une contrainte, ce qui signifie que si un programme déclare un champ de bits avec un type qui n’entre pas dans l’une des catégories ci-dessus, un diagnostic doit être imprimé.

Cependant, il est ensuite indiqué dans 6.7.2.1p10:

Un champ de bits est interprété comme ayant un type entier signé ou non signé composé du nombre de bits spécifié.

Je pense que ce qu’ils veulent dire, c’est que, bien que vous deviez déclarer le champ de bits avec quelque chose du type signed int x:n , le type de l’expression foo.x est un autre type d’entier signé, appelez-le T. T est un type entier signé composé de n bits et doit respecter les contraintes de tous les types entiers signés données dans la Sec. 6.2.6. Mais T n’est pas nécessairement compatible avec le type entier signé standard signé signed int . (En fait, la seule situation dans laquelle il est possible que T soit compatible avec ce type standard est si n est égal au nombre de bits dans un entier signé.)

Malheureusement, la norme ne fournit aucun moyen de nommer le type T; je ne vois donc pas comment il peut être utilisé dans un _Generic , du moins, pas de manière portable. Des implémentations spécifiques du langage C peuvent fournir un mécanisme pour nommer ce type, mais la norme ne les y oblige pas.

… le type actuel fait la différence en C11 avec _Generic

Les résultats de la façon dont un compilateur traite les types de champs sont indiqués ci-dessous.

Les champs 8 bits et 32 ​​bits correspondent aux suspects habituels.

Quel est le type d’un champ de bits à 1 bit? Comme d’autres l’ont mentionné, son “nom” n’est pas clairement spécifié, mais ce n’est pas l’un des types de normes attendus.

Cela ne cite pas une spécification, mais montre comment un compilateur respecté a interprété la spécification C.


GNU C11 (GCC) version 5.3.0 (i686-pc-cygwin)
compilé par GNU C version 5.3.0, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3

 #define info_typename(X) _Generic((X), \ _Bool: "_Bool", \ unsigned char: "unsigned char", \ signed char: "signed char", \ char: "char", \ int: "int", \ unsigned : "unsigned", \ default: "default" \ ) #define TYPE_NAME(t) { printf("%s\n", info_typename(t)); } #include  int main() { struct { signed int x1 :1; signed int x8 :8; signed int x32 :32; _Bool b; signed char sc; char c; unsigned char uc; int i; unsigned u; } foo; TYPE_NAME(foo.b); TYPE_NAME(foo.sc); TYPE_NAME(foo.c); TYPE_NAME(foo.uc); TYPE_NAME(foo.i); TYPE_NAME(foo.u); TYPE_NAME(foo.x1); TYPE_NAME(foo.x8); TYPE_NAME(foo.x32); } 

Sortie

 _Bool signed char char unsigned char int unsigned default (int:1) signed char (int:8) int (int:32) 

Notez avec -Wconversion et le code ci-dessous, je reçois cet avertissement. C’est donc au moins ce qu’un compilateur appelle son type de champ de petit bit: unsigned char:3 .

warning: la conversion de ‘unsigned int’ en ‘unsigned char: 3’ peut altérer sa valeur [-Wconversion]

  struct { unsigned int x3 :3; } foo; unsigned u = 1; foo.x3 = u; // warning 

Je soupçonne que cela dépend de:

  1. Le compilateur
  2. Paramètres d’optimisation
  3. Le nombre maximum de bits dans le champ de bits

Le type d’un champ de bits est:

champ de bits de type T

T est soit _Bool , int , signed int , unsigned int ou un type défini par l’implémentation.

Dans votre exemple, foo.x est de type: bit-field de type signed int .

Cela diffère de signed int parce que les deux types ne partagent pas les mêmes contraintes et exigences.

Par exemple:

 /* foo.x is of type bit-field of type signed int */ struct { signed int x:1; } foo; /* y is of type signed int */ signed int y; /* valid, taking the size of an expression of type signed int */ sizeof y; /* not valid, taking the size of an expression of type bit-field * of signed int */ sizeof foo.x; /* valid, taking the address of a lvalue of type signed int */ &y; /* invalid, taking the address of a lvalue of type bit-field * of signed int */ &foo.x;