在C语言结构体中使用压缩位域 - GCC

27

我正在Linux上使用C语言中的结构体。我开始使用位域和“packed”属性,但我遇到了奇怪的行为:

struct __attribute__((packed)) {
    int a:12;
    int b:32;
    int c:4;
} t1;

struct __attribute__((packed))  {
    int a:12;
    int b;
    int c:4;
}t2;

void main()
{
    printf("%d\n",sizeof(t1)); //output - 6
    printf("%d\n",sizeof(t2)); //output - 7
}

这两个结构体完全相同,为什么它们却要占用不同的字节数?


1
因为t2::b保证是一个独立的内存位置?考虑数据竞争。 - Kerrek SB
应使用%zu打印sizeof的结果。 - phuclv
2个回答

42

你的这两个结构体并不是“完全相同”的。第一个结构体有三个连续的位域,而第二个结构体有一个位域、一个非位域的 int 类型和第二个位域。

这一点非常重要:连续的(非零宽度)位域被合并为单个 内存位置,而位域后面跟一个非位域时则是不同的内存位置。

第一个结构体有一个内存位置,第二个结构体有三个。你可以获取第二个结构体中 b 成员的地址,但在第一个结构体中无法这样做。访问第二个结构体中的 b 成员与访问其 ac 成员不会相互干扰,但在第一个结构体中会产生竞争。

在位域成员后面放置一个非位域(或零长度的位域)可以在某种程度上“关闭”它,后面的部分将成为一个不同/独立的内存位置/对象。编译器不能像在第一个结构体中那样把你的 b 成员打包进位域里。


30
struct t1 // 6 bytes
{
    int a:12; // 0:11
    int b:32; // 12:43
    int c:4;  // 44:47
}__attribute__((packed));

struct t1 // 7 bytes
{
    int a:12; // 0:11
    int b;    // 16:47
    int c:4;  // 48:51
}__attribute__((packed));

常规的int b必须对齐到字节边界。 因此,在它之前会有填充。 如果您将c放在紧挨着a的位置,那么这个填充就不再需要了。建议这样做,因为访问非字节对齐的整数,如int b:32,速度很慢。


访问非字节对齐的整数(例如int b:32)会很慢,您有什么依据?“慢”是什么意思?难道不只是移位和屏蔽内存值吗? - Alexis
1
这取决于情况,但它永远不会像字节对齐一样快。有多慢(或者我们如何定义慢)取决于应用程序、编译器等因素。 - John Zwinck
明白了,我猜这只是快速移位+掩码的问题。 - Alexis
1
如果整个值适合于单个寄存器,则可能只需“shift+mask”。但是有很多可能性。也许它不适合单个寄存器(平台相关),因此需要2个移位,2个掩码以及在2个寄存器之间进行或操作。也许这种结构参与了一个本来可以矢量化的操作,但现在不能(或者可以,但需要进行未对齐的加载)。有很多可能性。 - Matthew M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接