Saltar a contenido

Última actualización:2026-10-12

Campos de bits en C++

Sobre el autor:

Cuenta pública personal de WeChat:

wechat

¿Qué es un campo de bits?

Un “campo de bits” o “segmento de bits” (bit field) es una estructura de datos que permite almacenar datos de forma compacta a nivel de bits y da al programador la posibilidad de manipular los bits de esa estructura. Una ventaja de esta estructura de datos es que ahorra espacio de almacenamiento en las unidades de datos; cuando el programa necesita miles o millones de unidades de datos, este método resulta especialmente importante. Una segunda ventaja es que los campos de bits permiten acceder de forma muy cómoda a partes de un valor entero, lo que simplifica el código fuente del programa. El inconveniente de esta estructura de datos es que la implementación de los campos de bits depende de la máquina y del sistema concretos, y puede dar resultados distintos en plataformas diferentes, lo que hace que los campos de bits sean, por naturaleza, no portables.

  • La disposición de los campos de bits en memoria depende de la máquina
  • El tipo de un campo de bits debe ser un tipo entero o de enumeración; el comportamiento de los campos de bits en tipos con signo depende de la implementación
  • El operador de dirección (&) no puede aplicarse a un campo de bits; ningún puntero puede apuntar a un campo de bits de una clase

Uso de los campos de bits

Los campos de bits se declaran normalmente mediante una estructura; esa declaración asigna un nombre a cada miembro del campo de bits y determina su anchura:

struct bit_field_name
{
    type member_name : width;
};
Elemento Descripción
bit_field_name Nombre de la estructura de campo de bits
type Tipo del miembro del campo de bits: debe ser int, signed int o unsigned int
member_name Nombre del miembro del campo de bits
width Número de bits que ocupa el miembro

Por ejemplo, declaramos el siguiente campo de bits:

struct _PRCODE
{
    unsigned int code1: 2;
    unsigned int cdde2: 2;
    unsigned int code3: 8;
};
struct _PRCODE prcode;
Esta definición hace que prcode contenga 2 campos de bits de 2 bits y 1 campo de bits de 8 bits; podemos asignarles valores mediante el operador de acceso a miembros de la estructura

prcode.code1 = 0;
prcode.code2 = 3;
procde.code3 = 102;

Al asignar hay que tener cuidado de que el valor no supere la capacidad del miembro del campo de bits; por ejemplo, prcode.code3 es un miembro de campo de bits de 8 bits, cuya capacidad es 2^8 = 256, es decir, el rango de asignación debe ser [0,255].

Tamaño y alineación de los campos de bits

Tamaño de los campos de bits

Por ejemplo, el siguiente campo de bits:

struct box 
{
    unsigned int a: 1;
    unsigned int  : 3;
    unsigned int b: 4;
};
En medio de esta estructura de campo de bits hay un campo de bits sin nombre que ocupa 3 bits y que solo sirve como relleno, sin ningún significado práctico. El relleno hace que la estructura utilice en total 8 bits. Pero el lenguaje C usa unsigned int como unidad básica de los campos de bits: aunque el único miembro de una estructura sea un campo de bits de 1 bit, el tamaño de esa estructura es el mismo que el de un unsigned int. En algunos sistemas unsigned int ocupa 16 bits, y en sistemas x86 ocupa 32 bits. En el resto del artículo se asume por defecto que unsigned int ocupa 32 bits.

Alineación de los campos de bits

Un miembro de campo de bits no puede cruzar la frontera de dos unsigned int; si el número total de bits declarados de los miembros supera el tamaño de un unsigned int, el compilador desplazará automáticamente los miembros del campo de bits para alinearlos según la frontera del unsigned int. Por ejemplo:

struct stuff 
{
    unsigned int field1: 30;
    unsigned int field2: 4;
    unsigned int field3: 3;
};

field1 + field2 = 34 bits, que supera los 32 bits; el compilador desplazará field2 para almacenarlo en la siguiente unidad de unsigned int, de modo que entre stuff.field1 y stuff.field2 quedará un hueco de 2 bits, y stuff.field3 irá justo después de stuff.field2; la estructura tiene ahora un tamaño de 2 * 32 = 64 bits.

Este hueco puede rellenarse con el miembro de campo de bits sin nombre mencionado antes; también podemos usar un miembro de campo de bits sin nombre de anchura 0 para que el siguiente miembro del campo de bits se alinee con el siguiente entero. Por ejemplo:

struct stuff 
{
    unsigned int field1: 30;
    unsigned int       : 2;
    unsigned int field2: 4;
    unsigned int       : 0;
    unsigned int field3: 3; 
};

Aquí hay un hueco de 2 bits entre stuff.field1 y stuff.field2, y stuff.field3 se almacena en el siguiente unsigned int; la estructura tiene ahora un tamaño de 3 * 32 = 96 bits.

Para ver el código de estudio, consulta: bit.cpp

Inicialización de los campos de bits y remapeo de bits

Inicialización

La inicialización de los campos de bits se hace igual que la de una estructura normal; aquí se enumeran dos formas, como sigue:

struct stuff s1= {20,8,6};
O bien asignando valores directamente a los miembros del campo de bits

struct stuff s1;
s1.field1 = 20;
s1.field2 = 8;
s1.field3 = 4;

Remapeo de los campos de bits (Re-mapping)

Declaramos un campo de bits de tamaño 32 bits

struct box {
    unsigned int ready:     2;
    unsigned int error:     2;
    unsigned int command:   4;
    unsigned int sector_no: 24;
}b1;

Poner a cero un campo de bits mediante el remapeo

int* p = (int *) &b1;  // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址" 
*p = 0;                // 清除 s1,将各成员归零

Utilizar una unión (union) para remapear un campo de bits de 32 bits a unsigned int

Primero presentamos brevemente qué es una unión

Una “unión” es un tipo especial de clase y también una estructura de datos de tipo compuesto. Dentro de una “unión” se pueden definir varios tipos de datos distintos; una variable declarada de ese tipo “unión” puede contener cualquiera de los datos definidos por la “unión”, y estos datos comparten el mismo segmento de memoria con el fin de ahorrar espacio.

Las “uniones” y las “estructuras” tienen ciertas similitudes, pero son esencialmente diferentes. En una estructura, cada miembro tiene su propio espacio de memoria y la longitud total de una variable de estructura es la suma de las longitudes de sus miembros (exceptuando las estructuras vacías y sin considerar el ajuste de bordes). En una “unión”, en cambio, los miembros comparten un mismo segmento de memoria y la longitud de una variable de unión es igual a la del miembro más largo. Cabe señalar que esta compartición no significa cargar varios miembros a la vez en una variable de unión, sino que la variable de unión puede tomar el valor de cualquiera de sus miembros, aunque solo uno cada vez: al asignar un nuevo valor se descarta el anterior.

Podemos declarar la siguiente unión:

union u_box {
  struct box st_box;     
  unsigned int ui_box;
};

En sistemas x86, tanto unsigned int como box ocupan 32 bits; mediante esta unión, st_box y ui_box comparten un mismo bloque de memoria. Qué bit del campo de bits corresponde a qué bit del unsigned int depende del compilador y del hardware. Para poner a cero el campo de bits utilizando la unión, el código es el siguiente:

union u_box u;
u.ui_box = 0;

Referencia de estudio: http://www.yuan-ji.me/C-C-%E4%BD%8D%E5%9F%9F-Bit-fields-%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/

Temas relacionados

Comentarios