使用位域和联合的意外行为

我正在尝试使用位域和联合,并创建了此代码:

union REG{
    struct{
        char posX: 7;
        char posY: 7;
        unsigned char dir:  2;
    };

    unsigned short reg;
};

当我运行sizeof( short )时,我得到2,但是当我运行sizeof( REG )时,我得到4。这对我来说很奇怪,因为当我对位求和时,我得到7 + 7 + 2 = 16 ,即2字节数据类型的位大小。

我目前正在使用带有编译器tdm-gcc 9.9.2 64-bit Debug的Dev-C ++编辑器。

这是我的第一个问题,所以请告诉我是否需要更多信息... 谢谢!

编辑:经过进一步的实验,当我将posX和posY的大小设置为6位时,我意识到大小是相同的(2字节)。但这仍然令人困惑,因为总和为14位,小于2个字节...

编辑2:多亏了Aviberger,我意识到用short / unsigned short替换char / unsigned char数据类型时,'''sizeof(REG)''''的结果变为2。但是我仍然不知道“为什么会这样?”

denny88613 回答:使用位域和联合的意外行为

根据规格,我们拥有

一个实现可以分配任何足够大的可寻址存储单元来容纳一个位, 领域。如果仍有足够的空间,则一个位域紧跟在一个 结构应打包到同一单元的相邻位中。如果空间不足, 是将不合适的位字段放入下一个单元还是与相邻单元重叠 实现定义的。单位内位域的分配顺序(从高到高) 低阶或从低阶到高阶)是实作定义。的对齐 未指定可寻址存储单元。

因此,实际行为取决于编译器为位字段选择的大小分配单位,以及它是否允许字段跨越多个分配单位。该选择是实现定义的,但是一个常见的实现是使用声明的位字段类型作为分配单位,并且不允许跨越分配单位边界。因此,当您使用(unisgned)char时,它将使用8位分配单元。这意味着不能将两个位域组合为一个字节(7 + 7> 8和7 + 2> 8),因此最终将占用3个分配单位(字节),然后将其舍入到4以进行对齐加上工会的空缺。

将位域大小更改为6时,现在第二个和第三个位域可以容纳一个字节(6 + 2 = 8),因此它仅占用两个分配单元。

当您将位域类型更改为short时,它使用16位分配单元,因此所有3个位域都可以容纳一个分配单元。

,

使用structunion时要注意几点。最常见的是,慷慨地填充字段以使其与CPU的字长对齐。

struct {
     char   c1;
     char   c2;
} s1;

似乎应该是两个字节的结构,但是令人惊讶的是sizeof (s1)通常不是2,而是4甚至8。甚至在1980年代使用16位计算机的情况下也是这样。

这是因为C和C ++编译器会将结构的每个char元素对齐到两个字节或四个字节的边界。我还没有看到结构元素与8字节边界对齐,但是还没有64位体系结构这么需要-

解决方案是调用一个编译选项来“打包结构”。既可以在编译器命令行上完成此操作,也可以在结构声明之前包含一个合适的#pragma选项:

#pragma pack(1)   // this particular syntax is valid for many compilers

struct {
     char  c11;
     char  c12;
} s2;

#pragma pack(4)
,

取自标准(n4835):

11.4.9位字段[class.bit]
1 [...]在类对象中分配位字段是由实现定义的。比特字段的对齐方式是由实现定义的。比特字段打包到某个可寻址的分配单元中。 [注意:位域跨越了某些机器上的分配单元,而不是在其他机器上。在某些机器上,位字段是从右到左分配的,在其他机器上是从左到右分配的。 —尾注]

如您所见,大小和对齐方式是实现定义的。因此,您可能会在其他编译器/平台上获得预期的行为,但是在您的编译器/平台上,将得到与预期不同的结果。

本文链接:https://www.f2er.com/2592671.html

大家都在问