Битовое поле
Объявляет элементы данных класса с явным размером в битах. Смежные элементы битового поля могут (или не могут) быть упакованы, чтобы совместно использовать отдельные байты.
Объявление битового поля это объявление элемента данных класса, который использует следующую форму объявления:
идентификатор (необязательно) атрибут (необязательно) : размер
|
(1) | ||||||||
идентификатор (необязательно) атрибут (необязательно) : размер инициализатор-фигурные-скобки-или-равно
|
(2) | (начиная с C++20) | |||||||
Тип битового поля представляется с помощью последовательности-спецификаторов-объявления синтаксиса объявления.
атрибуты | — | (начиная с C++11) необязательная последовательность любого количества атрибутов |
идентификатор | — | имя битового поля, которое было объявлено. Имя является необязательным: безымянные битовые поля вводят указанное количество битов заполнения. |
размер | — | целочисленное константное выражение со значением большим или равным нулю. Если больше нуля, то оно представляет собой количество бит, которые данное битовое поле будет занимать. Нулевое значение допустимо только для безымянных битовых полей и имеет специальное значение |
инициализатор-фигурные-скобки-или-равно | — | инициализатор элемента по умолчанию для использования с этим битовым полем |
Содержание |
[править] Объяснение
Тип битового поля может быть тольк�� целочисленным или (возможно, cv-квалифицированным) типом перечисления, безымянное битовое поле не может быть объявлено с cv-квалифицированным типом.
Битовое поле не может быть статическим элементом данных.
Не существует битовых полей, являющихся значениями prvalue: преобразование lvalue-в-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 <iostream> #include <cstdint> #include <bit> struct S { // обычно занимает 2 байта: unsigned char b1 : 3; // 1-е 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) // печатать LSB-сначала std::cout << (b & 1); std::cout << '\n'; }
Возможный вывод:
2 1110000011110111
Специальное безымянное битовое поле нулевого размера может принудительно разорвать выравнивание. Оно указывает, что следующее битовое поле начинается в начале его единицы распределения:
#include <iostream> struct S { // обычно занимает 2 байта: // 3 бита: значение b1 // 5 битов: не используется // 2 бита: значение b2 // 6 битов: не используется unsigned char b1 : 3; unsigned char :0; // начало нового байта unsigned char b2 : 6; unsigned char b3 : 2; }; int main() { std::cout << sizeof(S) << '\n'; // обычно выводит 2 // обычно печатается 1, если бы // не разрыв заполнения в строке 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" является инициализатором в скобках // или равенством int x2 : 8 { 42 }; // OK; "{ 42 }" является инициализатором в скобках // или равенством // неоднозначности int y1 : true ? 8 : a = 42; // OK; инициализатор в скобках или равенством отсутствует int y2 : true ? 8 : b = 42; // ошибка: невозможно присваивание const int int y3 : (true ? 8 : b) = 42; // OK; "= 42" является инициализатором в скобках // или равенством int z : 1 || new int { 0 }; // OK; инициализатор в скобках или равенством отсутствует }; |
(начиная с C++20) |
[править] Примечание
Следующие свойства битовых полей зависят от реализации:
- Значение, полученное в результате присвоения или инициализации знакового битового поля со значением, выходящим за пределы допустимого диапазона, или инкремента битового поля со знаком за пределами его диапазона.
- Всё про фактическое размещение деталей битовых полей внутри объекта класса
- Для примера, на некоторых платформах, битовые поля не объединяют байты, на других это допустимо
- Также, на некоторых платформах, битовые поля упакованы слева направо, на других справа налево
В языке программирования C, размер битового поля не может превышать размера типа, лежащего в его основе, и будь-то int битовые поля, которые явно не являются signed или unsigned будут знаковыми или беззнаковым в зависимости от реализации. Для примера, int b:3; может иметь диапазон значений 0..7 или -4..3 в C, но только последний вариант допускается в C++.
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
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:2023):
- 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]
[править] Смотри также
реализует битовый массив постоянной длины (класс) | |
компактный динамический битовый набор (специализация шаблона класса) | |
Манипуляции с битами (C++20) | утилиты для доступа, управления и обработки отдельных битов и битовых последовательностей |
Документация C по Битовые поля
|