在 cppreference article 中关于位字段的内容:
#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';
}
我没有成功地在网上搜索为什么如果
b2
被声明为 uint16_t
那么 b2
会邻接 u
,并且 b2
不会字节对齐。
请注意,文章说的是“通常”和“可能的输出”。
某些编译器(例如示例中考虑的编译器)不会在存储单元之间拆分位域。因此,如果以
unsigned char
为单位存储,则 3 + 2 + 6 位将无法容纳。但如果您使用 16 位字,它们将会都适合。
标准具体说:
[注 1: 位域在某些机器上跨分配单元,而不是 在其他人身上。在某些机器上,位域是从右到左分配的, 对其他人从左到右。 — 尾注]
如果您想要任何类型的可移植性,这使得它们不再那么有用。但是,如果您使用它们来建模硬件寄存器,那么可移植性可能就不再是问题了。