为什么GCC填充这个位域?

Exo*_*ist 5 c struct atomic padding 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; 
};
Run Code Online (Sandbox Code Playgroud)

当我检查sizeof(epochs)它给了我12.

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

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

oua*_*uah 13

volatile unsigned int epoch    : 1;
volatile unsigned int pulse    : 1;
volatile unsigned int active0  : 7;
volatile unsigned int active1  : 7;
Run Code Online (Sandbox Code Playgroud)

^ 32位(4字节)

volatile unsigned int counter0 : 24; 
Run Code Online (Sandbox Code Playgroud)

^ 32位(4字节)

volatile unsigned int counter1 : 24; 
Run Code Online (Sandbox Code Playgroud)

^ 32位(4字节)

所以多4个字节.

C说:

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

没有足够的空间来把24位(counter0)更以32位为单位(可能是大小unsigned int在您的系统)已持有的16位(epoch,pulse,active0,active1).

您可以使用uin64_t而不是使用unsigned int以64位为单位打包您的位字段,但无论您的系统是否支持,它都是实现定义的.

(C99,6.7.2.1p4)"位字段的类型应为_Bool,signed int,unsigned int 或其他实现定义类型的限定或非限定版本."

  • 我认为如果它注意到前四个字段使用16位然后明确表示将紧随其后的下一个24位跨越32位边界,则会更清楚.并且GCC可能避免这种情况,因为访问跨越边界的位字段将需要两个加载指令(取决于目标处理器)和附加的位操作指令来组合加载的值.跨越边界存储会更糟糕.因此,比特字段分配优先使用32位单元. (4认同)
  • 这个答案正是我所需要的.基于32位对齐排列我的字段也意味着我可以避免填充:counter0,epoch,active0,counter1,pulse,active1.这使它坚持64位. (2认同)