Champs de bits

De RAD Studio
Aller à : navigation, rechercher

Remonter à Structures - Index

Les champs de bits correspondent à un nombre spécifié de bits ayant ou pas un identificateur associé. Les champs de bits permettent de subdiviser des structures (structures, unions, classes) en parties nommées dont la taille est déterminée par l'utilisateur.

Déclaration de champs de bits

La largeur du champ de bit et l'identificateur facultatif sont déclarés de la manière suivante :

type-specifier <bitfield-id> : width;

En C++, spécificateur-type est bool, char, unsigned char, short, unsigned short, long, unsigned long, int, unsigned int, __int64 ou unsigned __int64. En ANSI C, spécificateur-type est int ou unsigned int.


L'expression largeur doit être présente et doit s'évaluer en une constante entière. En C++ , la largeur d'un champ de bits peut être déclarée de taille quelconque. En ANSI C, la largeur d'un champ de bits ne peut excéder la taille du type déclarée. Un champ de bits de longueur zéro passe à l'unité d'allocation suivante.

Si l'identificateur du champ de bits est omis, le nombre de bits spécifiés par largeur est alloué, mais le champ n'est pas accessible. Cela vous permet de correspondre à des masques de bits, par exemple pour des registres matériels dont certains bits sont inutilisés.


Il est possible de déclarer des champs de bits uniquement dans des structures, des unions et des classes. Ils sont accessibles avec le même sélecteur de membre ( . et -->) que celui utilisé pour les autres membres.

Limitations dans l'utilisation des champs de bits

Quand vous utilisez des champs de bits, tenez compte des problèmes suivants :

  • Le code n'est pas portable car l'organisation des bits à l'intérieur d'un octet et des celle des octets à l'intérieur d'un mot dépend du processeur.
  • Il n'est pas possible d'utiliser l'adresse d'un champ de bits ; l'expression &mastruct.x est illégale si x est un identificateur de champ de bits car rien n'assure que mastruct.x se trouve à une adresse d'octet.
  • Les champs de bits sont utilisés pour regrouper plusieurs variables dans une zone de données plus petite, mais ils obligent le compilateur à générer du code supplémentaire pour manipuler ces variables. Cela entraîne un coût en terme de taille de code et en temps d'exécution.

Il est possible de déclarer des champs de bits uniquement dans des structures, des unions et des classes. Ils sont accessibles avec le même sélecteur de membre ( . et ->) que celui utilisé pour les autres membres.

#define Nothing 0x00
#define bitOne 0x01
#define bitTwo 0x02
#define bitThree 0x04
#define bitFour 0x08
#define bitFive 0x10
#define bitSix 0x20
#define bitSeven 0x40
#define bitEight 0x80

qui peut s'utiliser pour écrire du code comme le suivant :

if (flags & bitOne) {...}    // is bit One turned on
flags |= bitTwo;               // turn bit Two on
flags &= ~bitThree;         // turn bit Three off

Vous pouvez utiliser le même mécanisme pour des champs de bits d'une taille quelconque.

Complément des champs de bits

En C++ , Si la largueur est supérieure à celle du type du champ de bits, le compilateur insère un complément d'une taille égale à celle de la taille demandée moins la taille du type du champ de bits. Ainsi, la déclaration :

struct mystruct
{
  int i : 40;
  int j : 8;
};

crée un stockage de a 32 bit pour 'i', un complément de 8 bits et un stockage de 8 bits pour 'j'. Pour optimiser l'accès, le compilateur traite 'i' comme une variable int normale et pas comme un champ de bits.

Disposition et alignement

Les champs de bits sont décomposés en groupes de champs de bits consécutifs du même type sans tenir compte du signe. Chaque groupe de champs de bits est aligné en utilisant l'alignement du type des membres du groupe. Cet alignement est déterminé par le type et par la valeur de l'alignement global (définit par l'option d'alignement d'octet –aN). Dans chaque groupe, le compilateur groupe les champs de bits dans des zones aussi grande que la taille du type des champs de bits. Néanmoins, un champ de bits ne peut chevaucher deux de ces zones. La taille de la structure totale est alignée en fonction de l'alignement en cours.

Exemple de disposition, de complément et d'alignement de champs de bits

Dans la déclaration C++ suivante, ma_struct contient six champs de bits de trois types différents, int, long et char:

struct my_struct
{
   int one : 8;
   unsigned int two : 16;
   unsigned long three : 8;
   long four : 16;
   long five : 16;
   char six : 4;
};

Les champs de bits 'un' et 'deux' sont regroupés dans la même zone de 32 bits.

Ensuite, le compilateur ajoute un complément, si nécessaire, en fonction de l'alignement en cours, et le type de trois, car le type change entre la déclaration des variables deux et trois. Par exemple, si l'alignement en cours se fait à l'octet (-a1), il n'y a pas besoin de complément ; mais si l'alignement est de 4 octets (-a4), alors un complément de 8 bits est inséré.

Ensuite, les variables trois, quatre et cinq sont toutes de type long. Les variables trois et quatre sont regroupées dans une zone de 32 bits, mais cinq ne peut être placé dans la même zone, car cela crée une zone de 40 bits ce qui dépasse les 32 bits alloués au type long. Pour commencer une nouvelle zone pour cinq, le compilateur n'insère pas de complément si l'alignement en cours se fait à l'octet, et il insère 8 bits de complément si l'alignement en cours est un dword (4 octets).

Avec la variable six, le type change à nouveau. Comme char est toujours aligné sur un octet, il n'y a pas besoin de complément. Pour forcer l'alignement pour toute la structure, le compilateur complète la dernière zone avec 4 bits de complément si l'alignement se fait à l'octet ou avec 12 bits si l'alignement dword est utilisé.

La taille totale de ma_struct est de 9 octets avec l'alignement à l'octet et de 12 octets avec l'alignement dword.

Pour obtenir de meilleurs résultats en utilisant des champs de bits, vous devez :


  • Trier les champs de bits par type.
  • Vérifier qu'ils sont regroupés dans des zones en les triant afin qu'aucun champ de bits ne chevauche une limite de zone.
  • Vérifier que la structure est, dans la mesure, du possible remplie.

Il est aussi conseillé de forcer un alignement à l'octet pour cette structure en utilisant "#pragma option -a1". Si vous voulez savoir la taille de votre structure, faites-la suivre d'un "#pragma sizeof(mastruct)", qui vous en donne la taille.

Utilisation de champs d'un bit signé

Pour un type signé d'un seul bit, les valeurs possibles sont 0 ou –1. Pour un type non signé d'un seul bit, les valeurs possibles sont 0 ou 1. Ainsi, si vous affectez "1" à un champ d'un bit signé, la valeur est évaluée en –1 (moins un).

Quand vous stockez les valeurs true et false dans un champ d'un seul bit de type signé, vous ne pouvez pas tester l'égalité avec true car les champs d'un bit signé ne peuvent contenir que les valeurs '0' et '-1', qui ne sont pas compatibles avec true et false. Vous pouvez par contre tester si la valeur est non-nulle.

Pour les types non signés, et bien entendu pour le type bool, tester l'égalité avec true fonctionne normalement.

Par conséquent :

struct mystruct
{
   int flag : 1;
} M;
int testing()
{
   M.flag = true;
   if(M.flag == true)
     printf("success");}

ne fonctionne pas, mais :

struct mystruct
{
   int flag : 1;
} M;
int testing()
{
   M.flag = true;
   if(M.flag)
     printf("success");
}

fonctionne parfaitement.

Remarques sur la compatibilité

Entre différentes versions du compilateur, il peut y avoir des modifications de l'alignement par défaut (par exemple dans un souci de compatibilité avec d'autres compilateurs. Cela peut donc modifier l'alignement des champs de bits. Vous n'avez donc pas de garanties sur la cohérence de l'alignement des champs de bits entre différentes versions du compilateur. Pour tester la compatibilité ascendante des champs de bits dans votre code vous pouvez ajouter une instruction assert pour vérifier si les structures ont la taille attendue.

Selon les spécification des langages C et C++, l'alignement et le stockage des champs de bits est défini par l'implémentation. Donc, des compilateurs différents peuvent aligner et stocker différemment des champs de bits. Si vous voulez un contrôle complet de la disposition de vos champs de bits, il est préférable d'écrire vos propres routines d'accès aux bits et de créer vos propres champs de bits.