位域
声明一个具有明确大小(以位为单位)的类数据成员。相邻的位域成员可以(或可能不会)打包在一起,以共享和跨越各个字节。
位域声明是一个类数据成员声明,它使用以下声明符
标识符 (可选) attr (可选) : size |
(1) | ||||||||
标识符 (可选) attr (可选) : size 大括号或等号初始化器 |
(2) | (自 C++20 起) | |||||||
decl-specifier-seq of the declaration syntax 引入了位域的类型。
attr | - | (自 C++11 起) 任意数量的 属性 的序列 |
标识符 | - | 正在声明的位域的名称。名称是可选的:无名位域引入指定数量的 填充位。 |
size | - | 一个 整数常量表达式,其值大于或等于零。当大于零时,这是此位域将占用的位数。值零仅允许用于无名位域,并且具有 特殊含义。 |
大括号或等号初始化器 | - | 用于此位域的 默认成员初始化器 |
内容 |
[编辑] 解释
位域的类型只能是整型或(可能是 cv 限定的)枚举类型,无名位域不能用 cv 限定的类型声明。
位域不能是 静态数据成员。
没有位域 纯右值:左值到右值转换始终生成一个位域的底层类型的对象。
位域中的位数设定了它可以保存的值范围的限制
#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
如果位域的指定大小大于其类型的 size,则该值将受到类型的限制:一个 std::uint8_t b : 1000; 仍然会保存 0 到 255 之间的值。额外的位是 填充位。
因为位域不一定从字节的开头开始,所以不能获取位域的地址。指向位域的指针和非 const 引用是不可能的。当从位域 初始化 const 引用 时,将创建一个临时对象(其类型是位域的类型),使用位域的值复制初始化,并且引用绑定到该临时对象。
位域没有 默认成员初始化器: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 起) |
[edit] 说明
位域的以下属性是实现定义的
- 用超出范围的值为带符号位域赋值或初始化,或将带符号位域递增到超出其范围时产生的值。
- 有关位域在类对象中实际分配细节的所有内容。
- 例如,在某些平台上,位域不会跨越字节,而在其他平台上则会跨越字节。
- 此外,在某些平台上,位域从左到右打包,而在其他平台上则从右到左打包。
在 C 编程语言中,位域的宽度不能超过底层类型的宽度,并且是否 int 未明确指定为 signed 或 unsigned 的位域是有符号还是无符号是实现定义的。例如,int b:3; 在 C 中可能具有 0..7 或 -4..3 的值范围,但在 C++ 中,只允许后一种选择。
[edit] 缺陷报告
以下行为更改缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确行为 |
---|---|---|---|
CWG 324 | C++98 | 未指定是否返回值 对位域的赋值是位域 |
为 可能返回左值的运算符添加了位域规范 |
CWG 739 | C++98 | 未声明为 signed 或 unsigned 的位域的符号是实现定义的 |
与底层类型一致 |
CWG 2229 | C++98 | 可以声明无名位域,其类型具有 cv 限定符 | 禁止 |
CWG 2511 | C++98 | 位域类型中不允许使用 cv 限定符 | 位域可以具有 cv 限定符 枚举类型 |
[edit] 参考
- 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]
[edit] 另请参阅
实现常量长度位数组 (类模板) | |
节省空间的动态位集 (类模板特化) | |
位操作 (C++20) | 访问、操作和处理单个位和位序列的实用程序 |
C 文档 for 位域
|