4

程序在 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 字节值。

4

2 回答 2

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)“如果有足够的空间,结构中紧跟在另一个位域之后的位域应被打包到同一单元的相邻位中”

没有足够的空间将24counter0unsigned intepochpulseactive0active1

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

(C99,6.7.2.1p4)“位域的类型应为 _Bool、signed int、unsigned int或其他一些实现定义的类型的限定或非限定版本。”

于 2013-10-11T16:41:26.540 回答
1

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

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

于 2013-10-11T17:47:15.573 回答