Namespaces
Variants

Bit-field

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Declara un miembro de datos de clase con tamaño explícito, en bits. Los miembros de campo de bits adyacentes pueden (o no) ser empaquetados para compartir y distribuirse entre los bytes individuales.

Una declaración de campo de bits es una declaración de miembro de datos de clase que utiliza el siguiente declarador:

identificador  (opcional) attr  (opcional) : tamaño (1)
identificador  (opcional) attr  (opcional) : tamaño inicializador-entre-llaves-o-igual (2) (desde C++20)

El tipo del campo de bits es introducido por la decl-specifier-seq de la sintaxis de declaración .

attr - (since C++11) secuencia de cualquier número de atributos
identifier - el nombre del campo de bits que se está declarando. El nombre es opcional: los campos de bits sin nombre introducen la cantidad especificada de bits de relleno .
size - una expresión constante integral con un valor mayor o igual a cero. Cuando es mayor que cero, este es el número de bits que ocupará este campo de bits. El valor cero solo se permite para campos de bits sin nombre y tiene un significado especial .
brace-or-equal-initializer - inicializador de miembro predeterminado que se utilizará con este campo de bits

Contenidos

Explicación

El tipo de un campo de bits solo puede ser integral (incluyendo bool ) o un tipo de enumeración (posiblemente calificado con cv), un campo de bits sin nombre no puede declararse con un tipo calificado con cv.

Un campo de bits no puede ser un miembro de datos estático .

No existen campos de bits prvalues : la conversión de lvalue a rvalue siempre produce un objeto del tipo subyacente del campo de bits.

El número de bits en un campo de bits establece el límite del rango de valores que puede contener:

#include <iostream>
struct S
{
    // campo sin signo de tres bits, los valores permitidos son 0...7
    unsigned int b : 3;
};
int main()
{
    S s = {6};
    ++s.b; // almacenar el valor 7 en el campo de bits
    std::cout << s.b << '\n';
    ++s.b; // el valor 8 no cabe en este campo de bits
    std::cout << s.b << '\n'; // formalmente definido por la implementación, típicamente 0
}

Salida posible:

7
0

Múltiples campos de bits adyacentes generalmente se empaquetan juntos (aunque este comportamiento está definido por la implementación):

#include <bit>
#include <cstdint>
#include <iostream>
struct S
{
    // normalmente ocupará 2 bytes:
    unsigned char b1 : 3; // primeros 3 bits (en el 1er byte) son b1
    unsigned char    : 2; // siguientes 2 bits (en el 1er byte) se bloquean como no utilizados
    unsigned char b2 : 6; // 6 bits para b2 - no caben en el 1er byte => inicia un 2do
    unsigned char b3 : 2; // 2 bits para b3 - siguientes (y últimos) bits en el 2do byte
};
int main()
{
    std::cout << sizeof(S) << '\n'; // normalmente imprime 2
    S s;
    // establecer valores de campo distinguibles
    s.b1 = 0b111;
    s.b2 = 0b101111;
    s.b3 = 0b11;
    // mostrar diseño de campos en S
    auto i = std::bit_cast<std::uint16_t>(s);
    // normalmente imprime 1110000011110111
    // desglose es:  └┬┘├┘└┬┘└─┬──┘└┤
    //                b1 u  a   b2  b3
    // donde "u" marca los no utilizados :2 especificados en la estructura, y
    // "a" marca el relleno agregado por el compilador para alinear el siguiente campo a byte.
    // La alineación a byte ocurre porque el tipo de b2 se declara como unsigned char;
    // si b2 se declarara como uint16_t no habría "a", b2 estaría contiguo a "u".
    for (auto b = i; b; b >>= 1) // imprimir primero el bit menos significativo
        std::cout << (b & 1);
    std::cout << '\n';
}

Salida posible:

2
1110000011110111

El campo de bits especial sin nombre de tamaño cero puede forzar la interrupción del relleno. Especifica que el siguiente campo de bits comienza al inicio de su unidad de asignación:

#include <iostream>
struct S
{
    // normalmente ocupará 2 bytes:
    // 3 bits: valor de b1
    // 5 bits: sin usar
    // 2 bits: valor de b2
    // 6 bits: sin usar
    unsigned char b1 : 3;
    unsigned char :0; // comenzar un nuevo byte
    unsigned char b2 : 2;
};
int main()
{
    std::cout << sizeof(S) << '\n'; // normalmente imprime 2
                                    // normalmente imprimiría 1 si no fuera por
                                    // la ruptura de relleno en la línea 11
}

Salida posible:

2

Si el tamaño especificado del campo de bits es mayor que el tamaño de su tipo, el valor está limitado por el tipo: un std:: uint8_t b : 1000 ; aún mantendría valores en el rango [ 0 , 255 ] . Los bits adicionales son bits de relleno .

Debido a que los campos de bits no necesariamente comienzan al principio de un byte, no se puede tomar la dirección de un campo de bits. No son posibles los punteros y las referencias no constantes a campos de bits. Cuando se inicializa una referencia const desde un campo de bits, se crea un temporal (su tipo es el tipo del campo de bits), se copia inicializado con el valor del campo de bits, y la referencia se vincula a ese temporal.

No existen inicializadores de miembros por defecto para campos de bits: int b : 1 = 0 ; y int b : 1 { 0 } no son válidas.

(hasta C++20)

En caso de ambigüedad entre el tamaño del campo de bits y el inicializador de miembro por defecto, se elige la secuencia más larga de tokens que forma un tamaño válido:

int a;
const int b = 0;
struct S
{
    // casos simples
    int x1 : 8 = 42; // OK; "= 42" es brace-or-equal-initializer
    int x2 : 8 {42}; // OK; "{42}" es brace-or-equal-initializer
    // ambigüedades
    int y1 : true ? 8 : a = 42;   // OK; brace-or-equal-initializer está ausente
    int y2 : true ? 8 : b = 42;   // error: no se puede asignar a const int
    int y3 : (true ? 8 : b) = 42; // OK; "= 42" es brace-or-equal-initializer
    int z : 1 || new int{0};      // OK; brace-or-equal-initializer está ausente
};
(desde C++20)

Notas

Las siguientes propiedades de los campos de bits son definidas por la implementación :

  • El valor que resulta de asignar o inicializar un campo de bits con signo con un valor fuera de rango, o de incrementar un campo de bits con signo más allá de su rango.
  • Todo sobre los detalles reales de asignación de campos de bits dentro del objeto de clase.
  • Por ejemplo, en algunas plataformas, los campos de bits no cruzan bytes, en otras sí lo hacen.
  • También, en algunas plataformas, los campos de bits se empaquetan de izquierda a derecha, en otras de derecha a izquierda.

En el lenguaje de programación C, el ancho de un campo de bits no puede exceder el ancho del tipo subyacente, y si los campos de bits int que no son explícitamente signed o unsigned son con signo o sin signo está definido por la implementación. Por ejemplo, int b : 3 ; puede tener el rango de valores [ 0 , 7 ] o [ - 4 , 3 ] en C, pero solo la última opción está permitida en C++.

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 324 C++98 no estaba especificado si el valor de retorno
de una asignación a un campo de bits es un campo de bits
se añadieron especificaciones de campo de bits para
operadores que pueden retornar lvalues
CWG 739 C++98 la signatura de campos de bits que no estaban declarados
signed ni unsigned estaba definida por la implementación
consistente con los tipos subyacentes
CWG 2229 C++98 los campos de bits sin nombre podían declararse con un tipo calificado con cv prohibido
CWG 2511 C++98 las calificaciones cv no estaban permitidas en tipos de campo de bits los campos de bits pueden tener tipos de enumeración
calificados con cv

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 11.4.10 Campos de bits [class.bit]
  • Estándar C++20 (ISO/IEC 14882:2020):
  • 11.4.9 Campos de bits [class.bit]
  • Estándar C++17 (ISO/IEC 14882:2017):
  • 12.2.4 Campos de bits [class.bit]
  • Estándar C++14 (ISO/IEC 14882:2014):
  • 9.6 Campos de bits [class.bit]
  • Estándar C++11 (ISO/IEC 14882:2011):
  • 9.6 Campos de bits [class.bit]
  • Estándar C++03 (ISO/IEC 14882:2003):
  • 9.6 Campos de bits [class.bit]
  • Estándar C++98 (ISO/IEC 14882:1998):
  • 9.6 Campos de bits [class.bit]

Véase también

implementa arreglo de bits de longitud constante
(class template)
bitset dinámico eficiente en espacio
(class template specialization)
Bit manipulation (C++20) utilidades para acceder, manipular y procesar bits individuales y secuencias de bits
C documentation para Bit-fields