位字段是对齐数据的一种方式吗



来自cppreference

#include <iostream>
struct S {
// will usually occupy 2 bytes:
// 3 bits: value of b1
// 2 bits: unused
// 6 bits: value of b2
// 2 bits: value of b3
// 3 bits: unused
unsigned char b1 : 3, : 2, b2 : 6, b3 : 2;
};
int main()
{
std::cout << sizeof(S) << 'n'; // usually prints 2
}

为什么这2位未使用?

不可能只是:

unsigned char b1 : 3, b2 : 6, b3 : 2;

这是一种填充方式吗?

有人能解释一下(如果我没有错的话(这个位字段是否真的改变了变量的大小,或者它们只是一个";建议";对于编译器?(作为inline关键字(。如果位字段如我所理解的那样工作,您将能够在1位上存储布尔值(这是不可能的,因为内存被拆分为1字节的块(

cppreference上的示例只是告诉标准中不能保证位字段被映射到相邻的内存区域,尽管大多数明智的实现都会这样做。提供的一个代码片段故意使用一个2位的未命名位文件来请求存储器中的两位,并且只是展示了位文件可以是未命名的。这使得表示该结构所需的比特总数为14。

14位可以打包成2个字节(16位(,所以这是对平均实现的期望,但不能保证会发生。

最后,但并非最不重要的是,如果您发现自己处于在结构上使用位字段的位置,请三思而后行。当它们真正提供优势时,很少有场景,所以你需要确保你正在处理其中之一。

您引用的示例用于解释多个位字段通常(不一定(打包在一起。如果你仔细看一下:

// 3 bits: value of b1      - 3 bits occupied
// 2 bits: unused           - 2 bits left unused
// 6 bits: value of b2      - 6 bits occupied (these 6 can't fit into the previous byte
as 5 bits are already occupied. What will happen in this 
case? Will these 6 bits go into the next byte? Will they
somehow *overlap* two bytes?
// 2 bits: value of b3       
// 3 bits: unused

如果sizeof(S) == 2的结果为真,那么我们可以说字段b2是横跨字节的。这个例子试图解释这个概念。

这个例子在解释这一点时不太清楚,所以让我创建一个稍微好一点的例子:

struct S {
unsigned char b1 : 3, : 2, b2 : 6, b3 : 3;
};

不同之处在于b3现在是3比特。结构的总位数=3 + 2 + 6 + 3 = 14。如果我现在打印sizeof (S),我得到3作为输出,这告诉我的系统上没有跨字节。

此外,你可以在页面底部找到这个:

  • 关于类对象中位字段的实际分配细节的一切
    • 例如,在某些平台上,位字段不跨字节,而在其他平台上则跨字节
    • 此外,在一些平台上,位字段从左到右打包,在另一些平台上从右到左打包

如果(如果我没有错的话(这个位字段实际上改变了变量的大小,或者它们只是一个";建议";对于编译器?(作为内联关键字(。如果位字段如我所理解的那样工作,您将能够在1位上存储布尔值(这是不可能的,因为内存被拆分为1字节的块(

几乎所有关于位字段的内容都是由实现定义的,因此要获得正确的答案,您必须查看每个编译器的文档并阅读ABI文档才能获得答案。例如,这来自GCC文档:

比特字段是否可以跨越存储单元边界(C90 6.5.2.1、C99和C11 6.7.2.1(。
由ABI确定。

如果我们查看为GCC 10.1生成的程序集,我们可以看到比特字段实际上正在使用:

# b1: 1, b2: 61, b3: 3
sub     rsp, 24
mov     eax, -767
lea     rdi, [rsp+14]
mov     WORD PTR [rsp+14], ax

二进制数字-767

b3  b6    b1
11 111101 00000001

1位上的布尔值

如果不提到尝试这样做的std::vector<bool>,答案就不完整,但事实证明这不是一个好主意。

最新更新