为什么GCC填充这个位域?

时间:2013-10-11 16:38:17

标签: c struct padding atomic compare-and-swap

程序在C中使用std = c99,这是在64位计算机上。

struct epochs {
    volatile unsigned int epoch    : 1;
    volatile unsigned int pulse    : 1;
    volatile unsigned int active0  : 7;
    volatile unsigned int active1  : 7;
    volatile unsigned int counter0 : 24; 
    volatile unsigned int counter1 : 24; 
};

当我检查sizeof(epochs)时,它给了我12个。

我可以告诉gcc不要通过添加__attribute((packed))来填充它;所以我可以解决它。但是,我真的想知道为什么要添加4个字节来填充这个64位结构?

这里的主要内容是这个结构需要64位,因为它在64位原子交换操作中一次更新,当然这对12字节值不起作用。

2 个答案:

答案 0 :(得分:13)

volatile unsigned int epoch    : 1;
volatile unsigned int pulse    : 1;
volatile unsigned int active0  : 7;
volatile unsigned int active1  : 7;

^ 32位(4字节)

volatile unsigned int counter0 : 24; 

^ 32位(4字节)

volatile unsigned int counter1 : 24; 

^ 32位(4字节)

再多4个字节。

C说:

  

(C99,6.7.2.1p10)"如果剩余足够的空间,紧接在结构中另一个位域之后的位域应被打包到同一单元的相邻位中,并且#34;

没有足够的空间将24位(counter0)放在一个32位单位(可能是系统中unsigned int的大小)中已经存在16位({{ 1}},epochpulseactive0)。

您可以使用active1代替使用uin64_t以64位为单位打包您的位字段,但无论您的系统是否支持,它都是实现定义的。

  

(C99,6.7.2.1p4)&#34;位字段的类型应为_Bool,signed int,unsigned int,或其他一些实现定义类型的限定或非限定版本< /强>&#34;

答案 1 :(得分:1)

虽然一些较旧的编译器曾将int foo:3;视为同义词,例如long foo:3short foo:3,只需以方便的方式放置foo,本C标准规定每个位字段必须完全适合相应大小的存储单元。我不知道该规范的基本原理是什么,因为指定位字段的方式仍然过于模糊,无法在便携式代码中使用,但有时无法以最佳方式打包。例如,24位值可以有效地存储在结构中的唯一方法是在一台支持32位整数的机器上有一台机器,或者有一个可以放在相邻的8位数据的机器。 24位值(前面或后面),以便“填写”一个32位字。

幸运的是,在您的特定情况下,可以通过重新排列字段来避免效率低下。如果您的编译器支持使用这种类型的位域,则可以通过将每个字段的声明类型更改为unsigned long long来避免效率低下[在这种情况下,如果位字段允许跨位32位边界,则可以允许它们没有跨越64位边界]。