Recherche…


Introduction

La plupart des variables de C ont une taille qui est un nombre entier d'octets. Les champs de bits font partie d'une structure qui n'occupe pas nécessairement un nombre entier d'octets; ils peuvent n'importe quel nombre de bits. Plusieurs champs de bits peuvent être regroupés dans une seule unité de stockage. Ils font partie de la norme C, mais de nombreux aspects sont définis pour la mise en œuvre. Ils sont l'une des parties les moins portables de C.

Syntaxe

  • identificateur de spécificateur de type: taille;

Paramètres

Paramètre La description
spécificateur de type signed , unsigned , int ou _Bool
identifiant Le nom de ce champ dans la structure
Taille Le nombre de bits à utiliser pour ce champ

Remarques

Les seuls types portables pour les champs de bits sont signed , unsigned ou _Bool . Le type plain int peut être utilisé, mais le standard indique (§6.7.2¶5) … pour les champs de bits, il est défini par l'implémentation si le spécificateur int désigne le même type que signed int ou le même type que unsigned int .

D'autres types d'entiers peuvent être autorisés par une implémentation spécifique, mais leur utilisation n'est pas portable.

Champs de bits

Un simple champ de bits peut être utilisé pour décrire des éléments pouvant comporter un nombre spécifique de bits.

struct encoderPosition {
   unsigned int encoderCounts : 23;
   unsigned int encoderTurns  : 4;
   unsigned int _reserved     : 5;
};

Dans cet exemple, nous considérons un codeur avec 23 bits de simple précision et 4 bits pour décrire le multi-tour. Les champs de bits sont souvent utilisés lors de l'interfaçage avec du matériel qui génère des données associées à un nombre spécifique de bits. Un autre exemple pourrait être la communication avec un FPGA, où le FPGA écrit des données dans votre mémoire en sections de 32 bits, ce qui permet des lectures de matériel:

struct FPGAInfo {
    union {
        struct bits {
            unsigned int bulb1On  : 1;
            unsigned int bulb2On  : 1;
            unsigned int bulb1Off : 1;
            unsigned int bulb2Off : 1;
            unsigned int jetOn    : 1;
        };
        unsigned int data;
   };
};

Pour cet exemple, nous avons montré une construction couramment utilisée pour pouvoir accéder aux données dans ses bits individuels, ou pour écrire le paquet de données dans son ensemble (en émulant ce que le FPGA peut faire). Nous pourrions alors accéder aux bits comme ceci:

FPGAInfo fInfo;
fInfo.data = 0xFF34F;
if (fInfo.bits.bulb1On) {
    printf("Bulb 1 is on\n");
}

Ceci est valable, mais conformément à la norme C99 6.7.2.1, article 10:

L'ordre d'attribution des champs de bits au sein d'une unité (d'ordre élevé à faible ou faible à élevé) est défini par la mise en œuvre.

Vous devez être conscient de l’endianness lors de la définition des champs de bits de cette manière. En tant que tel, il peut être nécessaire d’utiliser une directive de préprocesseur pour vérifier l’état de la machine. Un exemple de ceci suit:

typedef union {
    struct bits {
#if defined(WIN32) || defined(LITTLE_ENDIAN)
    uint8_t commFailure :1;
    uint8_t hardwareFailure :1;
    uint8_t _reserved :6;
#else
    uint8_t _reserved :6;
    uint8_t hardwareFailure :1;
    uint8_t commFailure :1;
#endif
    };
    uint8_t data;
} hardwareStatus;

Utilisation de champs de bits sous forme de petits entiers

#include <stdio.h>

int main(void)
{
    /* define a small bit-field that can hold values from 0 .. 7 */
    struct
    {
        unsigned int uint3: 3;
    } small;

    /* extract the right 3 bits from a value */
    unsigned int value = 255 - 2; /* Binary 11111101 */
    small.uint3 = value;          /* Binary      101 */
    printf("%d", small.uint3);

    /* This is in effect an infinite loop */
    for (small.uint3 = 0; small.uint3 < 8; small.uint3++)
    {
        printf("%d\n", small.uint3);
    }

    return 0;
}

Alignement du champ binaire

Les champs de bits permettent de déclarer des champs de structure plus petits que la largeur des caractères. Les champs de bits sont implémentés avec un masque de niveau octet ou de niveau mot. L'exemple suivant résulte en une structure de 8 octets.

struct C
{
    short s;            /* 2 bytes */
    char  c;            /* 1 byte */
    int   bit1 : 1;     /* 1 bit */
    int   nib  : 4;     /* 4 bits padded up to boundary of 8 bits. Thus 3 bits are padded */
    int   sept : 7;     /* 7 Bits septet, padded up to boundary of 32 bits. */
};

Les commentaires décrivent une disposition possible, mais comme la norme stipule que l’alignement de l’unité de stockage adressable n’est pas spécifié , d’autres dispositions sont également possibles.

Un champ de bit sans nom peut avoir n'importe quelle taille, mais il ne peut pas être initialisé ou référencé.

Un champ de bits de largeur nulle ne peut pas recevoir de nom et aligne le champ suivant sur la limite définie par le type de données du champ de bits. Ceci est réalisé en remplissant des bits entre les champs de bits.

La taille de la structure 'A' est de 1 octet.

struct A
{
    unsigned char c1 : 3;
    unsigned char c2 : 4;
    unsigned char c3 : 1;
};

Dans la structure B, le premier champ de bits sans nom saute 2 bits; le champ binaire de largeur zéro après c2 provoque le démarrage de c3 à partir de la limite du caractère (donc 3 bits sont ignorés entre c2 et c3 . Il y a 3 bits de remplissage après c4 . La taille de la structure est donc de 2 octets.

struct B
{
    unsigned char c1 : 1;
    unsigned char    : 2;    /* Skips 2 bits in the layout */
    unsigned char c2 : 2;
    unsigned char    : 0;    /* Causes padding up to next container boundary */ 
    unsigned char c3 : 4;
    unsigned char c4 : 1;
};

Quand les champs de bits sont-ils utiles?

Un champ de bits est utilisé pour regrouper de nombreuses variables en un seul objet, similaire à une structure. Cela permet une utilisation réduite de la mémoire et est particulièrement utile dans un environnement intégré.

 e.g. consider the following variables having the ranges as given below.
 a --> range 0 - 3
 b --> range 0 - 1
 c --> range 0 - 7
 d --> range 0 - 1
 e --> range 0 - 1

Si nous déclarons ces variables séparément, chacune doit comporter au moins un entier sur 8 bits et l’espace total requis sera de 5 octets. De plus, les variables n'utiliseront pas toute la plage d'un entier non signé de 8 bits (0-255). Ici, nous pouvons utiliser des champs de bits.

typedef struct {
   unsigned int a:2;
   unsigned int b:1;
   unsigned int c:3;
   unsigned int d:1;
   unsigned int e:1;
} bit_a;

Les champs de bits dans la structure sont accessibles de la même manière que toute autre structure. Le programmeur doit veiller à ce que les variables soient écrites dans la plage. En dehors des limites, le comportement est indéfini.

int main(void)
{
   bit_a bita_var;
   bita_var.a = 2;              // to write into element a
   printf ("%d",bita_var.a);    // to read from element a.
   return 0;
}

Souvent, le programmeur veut mettre à zéro l'ensemble des champs de bits. Cela peut être fait élément par élément, mais il existe une seconde méthode. Créez simplement une union de la structure ci-dessus avec un type non signé supérieur ou égal à la taille de la structure. Ensuite, l'ensemble des champs de bits peut être mis à zéro en remettant à zéro cet entier non signé.

typedef union {
    struct {
       unsigned int a:2;
       unsigned int b:1;
       unsigned int c:3;
       unsigned int d:1;
       unsigned int e:1;
    };
    uint8_t data;
} union_bit;    

L'utilisation est la suivante

int main(void)
{
   union_bit un_bit;
   un_bit.data = 0x00;        // clear the whole bit-field
   un_bit.a = 2;              // write into element a
   printf ("%d",un_bit.a);    // read from element a.
   return 0;
}

En conclusion, les champs de bits sont couramment utilisés dans les situations de contraintes de mémoire où vous avez beaucoup de variables qui peuvent prendre des plages limitées.

À ne pas faire pour les champs de bits

  1. Les tableaux de champs de bits, les pointeurs vers les champs de bits et les fonctions renvoyant des champs de bits ne sont pas autorisés.
  2. L'opérateur d'adresse (&) ne peut pas être appliqué aux membres du champ binaire.
  3. Le type de données d'un champ de bits doit être suffisamment large pour contenir la taille du champ.
  4. L'opérateur sizeof() ne peut pas être appliqué à un champ de bits.
  5. Il n'y a aucun moyen de créer un typedef pour un champ de bits isolé (bien que vous puissiez certainement créer un typedef pour une structure contenant des champs de bits).
typedef struct mybitfield
{
    unsigned char c1 : 20;   /* incorrect, see point 3 */
    unsigned char c2 : 4;    /* correct */
    unsigned char c3 : 1;
    unsigned int x[10]: 5;   /* incorrect, see point 1 */
} A;

int SomeFunction(void)
{
    // Somewhere in the code
    A a = { … };
    printf("Address of a.c2 is %p\n", &a.c2);      /* incorrect, see point 2 */
    printf("Size of a.c2 is %zu\n", sizeof(a.c2)); /* incorrect, see point 4 */
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow