Buscar..


Introducción

La mayoría de las variables en C tienen un tamaño que es un número entero de bytes. Los campos de bits son parte de una estructura que no necesariamente ocupa un número entero de bytes; Pueden cualquier número de bits. Se pueden empaquetar múltiples campos de bits en una sola unidad de almacenamiento. Forman parte del estándar C, pero hay muchos aspectos que están definidos por la implementación. Son una de las partes menos portátiles de C.

Sintaxis

  • identificador de especificador de tipo: tamaño;

Parámetros

Parámetro Descripción
especificador de tipo signed , unsigned , int o _Bool
identificador El nombre de este campo en la estructura.
tamaño El número de bits a utilizar para este campo.

Observaciones

Los únicos tipos portátiles para campos de bits son signed , unsigned o _Bool . Se puede usar el tipo int simple, pero el estándar dice (§6.7.2¶5) ... para campos de bits, está definido por la implementación si el especificador int designa el mismo tipo que signed int o el mismo tipo que unsigned int .

Una implementación específica puede permitir otros tipos de enteros, pero su uso no es portátil.

Campos de bits

Se puede usar un campo de bits simple para describir cosas que pueden tener un número específico de bits involucrados.

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

En este ejemplo, consideramos un codificador con 23 bits de precisión simple y 4 bits para describir el giro múltiple. Los campos de bits se utilizan a menudo al interactuar con hardware que genera datos asociados con un número específico de bits. Otro ejemplo podría ser la comunicación con un FPGA, donde el FPGA escribe datos en su memoria en secciones de 32 bits permitiendo lecturas de hardware:

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;
   };
};

Para este ejemplo, hemos mostrado un constructo comúnmente utilizado para poder acceder a los datos en sus bits individuales, o para escribir el paquete de datos en su totalidad (emulando lo que podría hacer el FPGA). Entonces podríamos acceder a los bits como este:

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

Esto es válido, pero según el estándar C99 6.7.2.1, artículo 10:

El orden de asignación de los campos de bits dentro de una unidad (orden alto a orden bajo o orden bajo a orden alto) está definido por la implementación.

Debe ser consciente de la endianidad al definir campos de bits de esta manera. Como tal, puede ser necesario usar una directiva de preprocesador para verificar la endianness de la máquina. Un ejemplo de esto sigue:

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;

Usando campos de bits como enteros pequeños

#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;
}

Alineación de campo de bits

Los campos de bits permiten declarar campos de estructura que son más pequeños que el ancho de caracteres. Los campos de bits se implementan con una máscara de nivel de palabra o nivel de byte. El siguiente ejemplo da como resultado una estructura de 8 bytes.

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. */
};

Los comentarios describen un posible diseño, pero como el estándar dice que la alineación de la unidad de almacenamiento direccionable no está especificada , también son posibles otros diseños.

Un campo de bits sin nombre puede ser de cualquier tamaño, pero no pueden ser inicializados o referenciados.

A un campo de bits de ancho cero no se le puede dar un nombre y alinea el siguiente campo con el límite definido por el tipo de datos del campo de bits. Esto se logra rellenando bits entre los campos de bits.

El tamaño de la estructura 'A' es de 1 byte.

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

En la estructura B, el primer campo de bits sin nombre salta 2 bits; el campo de bits de ancho cero después de c2 hace que c3 inicie desde el límite char (de modo que se omiten 3 bits entre c2 y c3 . Hay 3 bits de relleno después de c4 . Por lo tanto, el tamaño de la estructura es de 2 bytes.

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;
};

¿Cuándo son útiles los campos de bits?

Un campo de bits se utiliza para agrupar muchas variables en un objeto, similar a una estructura. Esto permite un uso reducido de la memoria y es especialmente útil en un entorno integrado.

 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 declaramos estas variables por separado, cada una debe ser al menos un entero de 8 bits y el espacio total requerido será de 5 bytes. Además, las variables no utilizarán el rango completo de un entero sin signo de 8 bits (0-255). Aquí podemos utilizar los campos 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;

Se accede a los campos de bits de la estructura de la misma manera que a cualquier otra estructura. El programador debe tener cuidado de que las variables estén escritas en rango. Si está fuera de rango el comportamiento es indefinido.

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;
}

A menudo, el programador quiere poner a cero el conjunto de campos de bits. Esto se puede hacer elemento por elemento, pero hay un segundo método. Simplemente cree una unión de la estructura anterior con un tipo sin signo que sea mayor o igual que el tamaño de la estructura. Luego, todo el conjunto de campos de bits puede ponerse a cero poniendo a cero este entero sin signo.

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;    

El uso es el siguiente

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 conclusión, los campos de bits se usan comúnmente en situaciones de memoria restringida donde tiene muchas variables que pueden tomar rangos limitados.

No hacer para campos de bits

  1. Las matrices de campos de bits, punteros a campos de bits y funciones que devuelven campos de bits no están permitidas.
  2. El operador de dirección (&) no se puede aplicar a los miembros de campo de bits.
  3. El tipo de datos de un campo de bits debe ser lo suficientemente ancho para contener el tamaño del campo.
  4. El operador sizeof() no se puede aplicar a un campo de bits.
  5. No hay forma de crear un typedef para un campo de bits aislado (aunque ciertamente puede crear un typedef para una estructura que contiene campos 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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow