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

Объявляет член данных класса с явным размером в битах. Смежные битовые поля могут (или не могут) быть упакованы для совместного использования и распределения по отдельным байтам.

Объявление битового поля — это объявление элемента данных класса , которое использует следующий декларатор:

identifier  (необязательно) attr  (необязательно) : size (1)
identifier  (необязательно) attr  (необязательно) : size brace-or-equal-initializer (2) (since C++20)

Тип битового поля задаётся decl-specifier-seq в синтаксисе объявления .

attr - (since C++11) последовательность любого количества attributes
identifier - имя объявляемого битового поля. Имя является необязательным: безымянные битовые поля вводят указанное количество padding bits .
size - целочисленное integral constant expression со значением больше или равным нулю. Когда значение больше нуля, это количество битов, которое будет занимать данное битовое поле. Значение ноль разрешено только для безымянных битовых полей и имеет специальное значение .
brace-or-equal-initializer - default member initializer для использования с этим битовым полем

Содержание

Объяснение

Тип битового поля может быть только целочисленным (включая bool ) или (возможно cv-квалифицированным) перечислением, безымянное битовое поле не может быть объявлено с cv-квалифицированным типом.

Битовое поле не может быть статическим элементом данных .

Не существует битовых полей prvalues : преобразование lvalue-to-rvalue всегда производит объект базового типа битового поля.

Количество битов в битовом поле устанавливает предел диапазона значений, которые оно может хранить:

#include <iostream>
struct S
{
    // трёхбитное беззнаковое поле, допустимые значения 0...7
    unsigned int b : 3;
};
int main()
{
    S s = {6};
    ++s.b; // сохраняем значение 7 в битовом поле
    std::cout << s.b << '\n';
    ++s.b; // значение 8 не помещается в это битовое поле
    std::cout << s.b << '\n'; // формально определяется реализацией, обычно 0
}

Возможный вывод:

7
0

Несколько смежных битовых полей обычно упаковываются вместе (хотя это поведение определяется реализацией):

#include <bit>
#include <cstdint>
#include <iostream>
struct S
{
    // обычно занимает 2 байта:
    unsigned char b1 : 3; // первые 3 бита (в 1-м байте) - это b1
    unsigned char    : 2; // следующие 2 бита (в 1-м байте) заблокированы как неиспользуемые
    unsigned char b2 : 6; // 6 бит для b2 - не помещается в 1-й байт => начинается 2-й
    unsigned char b3 : 2; // 2 бита для b3 - следующие (и последние) биты во 2-м байте
};
int main()
{
    std::cout << sizeof(S) << '\n'; // обычно выводит 2
    S s;
    // установить различимые значения полей
    s.b1 = 0b111;
    s.b2 = 0b101111;
    s.b3 = 0b11;
    // показать расположение полей в S
    auto i = std::bit_cast<std::uint16_t>(s);
    // обычно выводит 1110000011110111
    // разбивка:  └┬┘├┘└┬┘└─┬──┘└┤
    //                b1 u  a   b2  b3
    // где "u" обозначает неиспользуемые :2, указанные в структуре, и
    // "a" обозначает добавленное компилятором выравнивание для байтового выравнивания следующего поля.
    // Байтовое выравнивание происходит потому, что тип b2 объявлен как unsigned char;
    // если бы b2 был объявлен как uint16_t, не было бы "a", и b2 примыкал бы к "u".
    for (auto b = i; b; b >>= 1) // вывод младшим битом вперед
        std::cout << (b & 1);
    std::cout << '\n';
}

Возможный вывод:

2
1110000011110111

Специальное безымянное битовое поле нулевого размера может быть использовано для принудительного разрыва выравнивания. Оно указывает, что следующее битовое поле начинается в начале своей единицы распределения:

#include <iostream>
struct S
{
    // will usually occupy 2 bytes:
    // 3 bits: value of b1
    // 5 bits: unused
    // 2 bits: value of b2
    // 6 bits: unused
    unsigned char b1 : 3;
    unsigned char :0; // start a new byte
    unsigned char b2 : 2;
};
int main()
{
    std::cout << sizeof(S) << '\n'; // usually prints 2
                                    // would usually print 1 if not for
                                    // the padding break in line 11
}

Возможный вывод:

2

Если указанный размер битового поля больше размера его типа, значение ограничивается типом: std:: uint8_t b : 1000 ; всё равно будет хранить значения в диапазоне [ 0 , 255 ] . Дополнительные биты являются битами заполнения .

Поскольку битовые поля не обязательно начинаются с начала байта, адрес битового поля не может быть взят. Указатели и неконстантные ссылки на битовые поля невозможны. При инициализации константной ссылки из битового поля создается временный объект (его тип соответствует типу битового поля), копирующий инициализированный значением битового поля, и ссылка привязывается к этому временному объекту.

Для битовых полей отсутствуют инициализаторы членов по умолчанию : int b : 1 = 0 ; и int b : 1 { 0 } являются некорректными.

(до C++20)

В случае неоднозначности между размером битового поля и инициализатором члена по умолчанию выбирается самая длинная последовательность токенов, образующая допустимый размер:

int a;
const int b = 0;
struct S
{
    // простые случаи
    int x1 : 8 = 42; // OK; "= 42" является brace-or-equal-initializer
    int x2 : 8 {42}; // OK; "{42}" является brace-or-equal-initializer
    // неоднозначности
    int y1 : true ? 8 : a = 42;   // OK; brace-or-equal-initializer отсутствует
    int y2 : true ? 8 : b = 42;   // ошибка: невозможно присвоить значение const int
    int y3 : (true ? 8 : b) = 42; // OK; "= 42" является brace-or-equal-initializer
    int z : 1 || new int{0};      // OK; brace-or-equal-initializer отсутствует
};
(начиная с C++20)

Примечания

Следующие свойства битовых полей являются определяемыми реализацией :

  • Значение, которое получается при присваивании или инициализации знакового битового поля значением вне диапазона, или при увеличении знакового битового поля за пределы его диапазона.
  • Все детали фактического распределения битовых полей внутри объекта класса.
  • Например, на некоторых платформах битовые поля не пересекают границы байтов, а на других — пересекают.
  • Также, на некоторых платформах битовые поля упаковываются слева направо, а на других — справа налево.

В языке программирования C ширина битового поля не может превышать ширину базового типа, и то, являются ли int битовые поля, которые не объявлены явно как signed или unsigned знаковыми или беззнаковыми, определяется реализацией. Например, int b : 3 ; может иметь диапазон значений [ 0 , 7 ] или [ - 4 , 3 ] в C, но в C++ допустим только последний вариант.

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
CWG 324 C++98 не было указано, является ли возвращаемое значение
присваивания битовому полю битовым полем
добавлены спецификации для битовых полей
для операторов, которые могут возвращать lvalues
CWG 739 C++98 знаковость битовых полей, которые не объявлены
signed ни unsigned была определяемой реализацией
согласовано с базовыми типами
CWG 2229 C++98 безымянные битовые поля могли объявляться с cv-квалифицированным типом запрещено
CWG 2511 C++98 cv-квалификации не допускались в типах битовых полей битовые поля могут иметь cv-квалифицированные
перечислимые типы

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 11.4.10 Битовые поля [class.bit]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 11.4.9 Битовые поля [class.bit]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 12.2.4 Битовые поля [class.bit]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 9.6 Битовые поля [class.bit]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 9.6 Битовые поля [class.bit]
  • Стандарт C++03 (ISO/IEC 14882:2003):
  • 9.6 Битовые поля [class.bit]
  • Стандарт C++98 (ISO/IEC 14882:1998):
  • 9.6 Битовые поля [class.bit]

Смотрите также

реализует битовый массив постоянной длины
(шаблон класса)
эффективное по памяти динамическое битовое множество
(специализация шаблона класса)
Bit manipulation (C++20) утилиты для доступа, манипуляции и обработки отдельных битов и битовых последовательностей
C documentation для Bit-fields