Spec-Zone.ru › C++

Битовое поле

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

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

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

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

attr - (начиная с C++11) последовательность из любого количества атрибутов
identifier - имя объявляемого битового поля. Имя необязательно: безымянные битовые поля вводят указанное количество битов заполнения.
size - целочисленное константное выражение со значением, большим или равным нулю. Если значение больше нуля, оно задаёт количество битов, занимаемых этим битовым полем. Нулевое значение разрешено только для безымянных битовых полей и имеет особое значение.
brace-or-equal-initializer - инициализатор элемента класса по умолчанию, используемый для этого битового поля

Пояснение

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

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

У битовых полей не бывает prvalue: преобразование lvalue в rvalue всегда создаёт объект базового типа битового поля.

Количество битов в битовом поле ограничивает диапазон представимых им значений:

#include <iostream>

struct S
{
    // three-bit unsigned field, allowed values are 0...7
    unsigned int b : 3;
};

int main()
{
    S s = {6};
    
    ++s.b; // store the value 7 in the bit-field
    std::cout << s.b << '\n';
    
    ++s.b; // the value 8 does not fit in this bit-field
    std::cout << s.b << '\n'; // formally implementation-defined, typically 0
}

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

7
0

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

#include <bit>
#include <cstdint>
#include <iostream>

struct S
{
    // will usually occupy 2 bytes:
    unsigned char b1 : 3; // 1st 3 bits (in 1st byte) are b1
    unsigned char    : 2; // next 2 bits (in 1st byte) are blocked out as unused
    unsigned char b2 : 6; // 6 bits for b2 - doesn't fit into the 1st byte => starts a 2nd
    unsigned char b3 : 2; // 2 bits for b3 - next (and final) bits in the 2nd byte
};

int main()
{
    std::cout << sizeof(S) << '\n'; // usually prints 2

    S s;
    // set distinguishable field values
    s.b1 = 0b111;
    s.b2 = 0b101111;
    s.b3 = 0b11;

    // show layout of fields in S
    auto i = std::bit_cast<std::uint16_t>(s);
    // usually prints 1110000011110111
    // breakdown is:  └┬┘├┘└┬┘└─┬──┘└┤
    //                b1 u  a   b2  b3
    // where “u” marks the unused :2 specified in the struct, and
    // “a” marks compiler-added padding to byte-align the next field.
    // Byte-alignment is happening because b2's type is declared unsigned char;
    // if b2 were declared uint16_t there would be no “a”, b2 would abut “u”.
    for (auto b = i; b; b >>= 1) // print LSB-first
        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
{
    // simple cases
    int x1 : 8 = 42; // OK; "= 42" is brace-or-equal-initializer
    int x2 : 8 {42}; // OK; "{42}" is brace-or-equal-initializer

    // ambiguities
    int y1 : true ? 8 : a = 42;   // OK; brace-or-equal-initializer is absent
    int y2 : true ? 8 : b = 42;   // error: cannot assign to const int
    int y3 : (true ? 8 : b) = 42; // OK; "= 42" is brace-or-equal-initializer
    int z : 1 || new int{0};      // OK; brace-or-equal-initializer is absent
};
(начиная с C++20)

Примечания

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

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

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

Отчёты об ошибках

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

DR Применён к Опубликованное поведение Корректное поведение
CWG 324 C++98 не было определено, является ли возвращаемое значение
присваивания битовому полю битовым полем
добавлены уточнения о битовых полях для
операторов, которые могут возвращать lvalue
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]

См. также

bitset
реализует битовый массив постоянной длины
(шаблон класса)
vector<bool>
динамический битовый набор с эффективным использованием памяти
(специализация шаблона класса)
Манипуляции с битами (C++20) утилиты для доступа к отдельным битам и последовательностям битов, их изменения и обработки
Документация по C о битовых полях

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/bit_field

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API