Поле битовой маски
Объявляет член данных класса со явным размером в битах. Смежные члены битовой маски могут (или не могут) быть упакованы для совместного использования и перекрытия отдельных байтов.
Объявление битовой маски — это объявление члена данных класса, которое использует следующий декларатор:
идентификатор (необязательно) атрибут (необязательно) : размер | (1) | |
идентификатор (необязательно) атрибут (необязательно) : размер инициализатор_скобки_или_равно | (2) | (с C++20) |
Тип битовой маски вводится последовательностью_спецификаторов_типа синтаксиса объявления.
| атрибут | - | (с C++11) последовательность любого количества атрибутов |
| идентификатор | - | имя битовой маски, которая объявляется. Имя необязательно: безымянные битовые маски вводят указанное количество битов_заполнения. |
| размер | - | целочисленное константное выражение со значением больше или равно нулю. При значении больше нуля это количество битов, которое будет занимать эта битовая маска. Значение ноль разрешено только для безымянных битовых масок и имеет специальное значение. |
| инициализатор_скобки_или_равно | - | инициализатор_члена_по_умолчанию, который будет использоваться с этой битовой маской |
Объяснение
Тип битовой маски может быть только целочисленным или (возможно, с квалификаторами cv) типом перечисления, безымянную битовую маску нельзя объявить с типом, имеющим квалификаторы cv.
Битовая маска не может быть статическим членом данных.
Для битовых масок нет временных значений: преобразование 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. Дополнительные биты являются битами_заполнения.
Поскольку битовые маски не обязательно начинаются с начала байта, адрес битовой маски нельзя получить. Указатели и ссылки на неконстантные битовые маски недопустимы. При инициализации константной ссылки из битовой маски создаётся временный объект (его тип — тип битовой маски), копируемый по значению из битовой маски, и ссылка привязывается к этому временному объекту.
| Для битовых масок нет инициализаторов_членов_по_умолчанию: | (до 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; может иметь диапазон значений 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 для Битовых полей | |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/bit_field