Accueil ⇒ Informatique ⇒ Langage C ⇒ Champs de bits

Champs de bits

Les champs de bits constituent un concept souvent méconnu du langage C, d'une part parce que leur utilisation tient de l'optimisation et d'autre part car ils sont absents de la majorité des sources de documentations des néophytes, à savoir les livres "pratiques" et les sites internet. Ils ne sont pourtant pas négligeables et peuvent servir dans bien des situations, comme nous l'allons voir...

Présentation

Il arrive souvent que l'on occupe davantage d'espace mémoire que nécessaire dans une application. L'exemple le plus courrant est celui du type bool du C++ : un booléen vaut soit 1, soit 0, ce qui correspond à un unique bit, néanmoins le type bool occupe un octet en mémoire, soit 7 bits de plus qu'il n'en faut. Autant dire que, dans le cas d'une matrice de booléens comportant des milliards de milliards d'éléments, cette perte d'espace se ressent nettement. Pour les booléens, la solution la plus courramment utilisée afin de pallier à ce problème est le vecteur de bits (cf. article correspondant sur le site), mais le principe des champs de bits est justement d'éviter ce gaspillage mémoire.

Un champ de bit, ou "bitfield" in english, est un entier constitué d'un nombre donnés de bits mémoire et membre d'une structure ou d'une union. La déclaration d'un tel champ est de la forme suivant :

type [identificateur] : taille;

type est soit unsigned int, soit signed int, identificateur est le nom du champ de bits (facultatif) et taille le nombre de bits occupés en mémoire par ce champ. Comme le lecteur l'aura probablement constaté, l'utilisation des champs de bits requiert une bonne maîtrise de la base 2 et de la représentation des données en mémoire (voir articles correspondants).

Utilisation

Les champs de bits peuvent se voir utilisés au sein de structures ou d'unions uniquement, et leur type ne correspond donc qu'à des entiers (signés ou non). Cet aspect peut apparaître un peu restrictif de prime abord, mais il ne faut pas oublier que tout type de variable en C peut s'utiliser comme un entier, et réciproquement (cf. char). Voici donc un exemple d'utilisation de champs de bits dans une structure :

struct {
    unsigned int b0_2 : 3;
    signed int b3_6 : 4;
    unsigned int : 1;
    unsigned int b8_15 : 8;
} bitfield;

La variable bitfield de type structure ci-dessus occupe au moins deux octets (nous verrons plus loin qu'elle en occupera généralement 4 en pratique). Elle est divisée en 4 champs de bits : la première variable, bitfield.b0_2 , occupe les 3 bits de poids faible, la variable bitfield.b3_6 occupe les quatre bits suivants et la variable bitfield.b8_15 occupe les bits 8 à 15. Le troisième champ n'est pas nommé et permet de définir un espace inutilisé d'un bit au sein de la structure, comme le décrit l'illustration suivante :

Exemple

Les champs de bits non signés sont interprétés comme des entiers non signés. De même, ceux signés peuvent être négatifs dans le codage en complément à 2. Ainsi, dans l'exemple ci-dessus, bitfield.b0_2 peut prendre des valeurs de l'intervalle [0 ; 7] tandis que bitfield.b3_6 peut se voir affectée des valeurs de [-8 ; 7]. En pratique, accéder à ces champs s'effectue exactement de la même manière qu'accéder aux champs d'une structure, soit :

/* Code bon */
bitfield.b8_15 = 42;
bitfield.b3_6 = (-6);
bitfield.b0_2 = 4;
 
/* Overflow */
bitfield.b3_6 = 10;

Précisions

Un champ de bit est stocké en mémoire dans un mot machine, qui est l'unité de stockage élémentaire, généralement de longueur 32 bits ( sizeof(int) ). Ainsi, la taille d'un tel champ ne peut excéder celle d'un mot machine. Si un champ de bit est assez petit pour laisser un espace suffisant, d'autre champs peuvent être alloués dans la même unité de stockage (structure ou union). Un champ de bit de taille nulle est un cas particulier : il indique que le champ de bit suivant doit être placé dans un autre mot machine, qu'il reste ou non de l'espace vacant dans l'unité de stockage courante.

Attention : on ne peut pas utiliser l'opérateur d'adresse avec des champs de bits.

Il est aussi important de noter que l'on ne peut pas utiliser l'opérateur d'adresse (&) avec des champs de bits, même s'il est possible de l'appliquer à une variable de type structure ou union en contenant.

Conclusion

Ainsi s'achève cette introduction à l'utilisation des champs de bits du langage C. Ceux-ci peuvent apporter d'importantes optimisations au niveau de l'utilisation mémoire ou des performances, même s'ils peuvent également se révéler sources de problèmes de portabilités entre des architectures différentes. Un outil formidable a utiliser judicieusement.