【发布时间】:2013-04-05 12:36:52
【问题描述】:
首先,引用 2011 ISO C 标准的N1570 草案,第 6.7.2.1 节,这是 ISO C 标准对位字段的说明:
位域的类型应为合格或不合格
_Bool、signed int、unsigned int或其他的版本 实现定义的类型。是否由实现定义 允许使用原子类型。
...
位域被解释为有符号或无符号整数类型 由指定的位数组成。如果值 0 或 1 是 存储到
_Bool类型的非零宽度位域中,值为 位域应与存储的值相比较;_Bool位域具有_Bool的语义。一个实现可以分配任何大的可寻址存储单元 足以容纳一个位域。如果有足够的空间,一个位域 紧跟在结构中的另一个位字段之后应被打包 到同一单元的相邻位。如果剩余空间不足, 是否将不适合的位域放入下一个单元或 重叠相邻单元是实现定义的。的顺序 一个单元内的位域分配(从高位到低位或 低阶到高阶)是实现定义的。对齐方式 未指定可寻址存储单元。
对于任何struct类型,该类型的对齐至少是该类型任何成员的最大对齐,并且任何类型的大小是其对齐的倍数。例如,如果结构包含(非位域)int 成员,而int 需要 4 字节对齐,则结构本身需要 4 字节或更多对齐。
许多编译器允许除_Bool 和int 类型之外的整数类型的位字段。
对于至少某些编译器,包含位域的struct 的对齐方式至少是位域的声明类型 的对齐方式。例如,对于 x86_64 上的 gcc 4.7.2,给出:
struct sb {
_Bool bf:1;
};
struct si {
unsigned bf:1;
};
gcc 为 struct sb 提供 1 字节的大小和对齐方式(这是 _Bool 的大小和对齐方式),并为 struct si 提供 4 字节的大小和对齐方式(这是 int 的大小和对齐方式)。它对实现定义类型的位字段做同样的事情;定义为long long bf:1; 的位字段强制封闭结构的大小为 8 字节并对齐。即使在这两种情况下,位字段bf 都是宽度仅为 1 位的对象,也会这样做。
我在 SPARC/Solaris 9 上使用 Sun 的编译器看到了类似的行为。
实验表明,定义为_Bool 或unsigned 的多个位域可以打包到单个字节内的相邻位中(实际上这是必需的),因此位域本身没有严格的对齐要求。
我知道结构成员的布局很大程度上是实现定义的,我不认为 gcc 的行为违反了 C 标准。
所以我的问题(终于!)是,为什么 gcc(连同至少一个不相关的 C 编译器,可能还有更多)这样做? gcc 的作者是否假设位字段的声明类型必须影响包含结构的大小和对齐方式?他们在这个假设中正确吗?我错过了 C 标准本身的要求吗?
这是一个展示该行为的测试程序。如果你想在你的系统上运行它,你可能需要注释掉它的一部分,如果你使用的是不支持某些新特性的旧编译器,或者不允许某些类型的位字段。我很想知道是否有编译器不表现得像 gcc。
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
int main(void) {
struct sb { _Bool bf:1; };
struct s8 { uint8_t bf:1; };
struct s16 { uint16_t bf:1; };
struct s32 { uint32_t bf:1; };
struct s64 { uint64_t bf:1; };
printf("sizeof (struct sb) = %2zu (%2zu bits)\n",
sizeof (struct sb),
sizeof (struct sb) * CHAR_BIT);
printf("sizeof (struct s8) = %2zu (%2zu bits)\n",
sizeof (struct s8),
sizeof (struct s8) * CHAR_BIT);
printf("sizeof (struct s16) = %2zu (%2zu bits)\n",
sizeof (struct s16),
sizeof (struct s16) * CHAR_BIT);
printf("sizeof (struct s32) = %2zu (%2zu bits)\n",
sizeof (struct s32),
sizeof (struct s32) * CHAR_BIT);
printf("sizeof (struct s64) = %2zu (%2zu bits)\n",
sizeof (struct s64),
sizeof (struct s64) * CHAR_BIT);
return 0;
}
这是我在系统上得到的输出:
sizeof (struct sb) = 1 ( 8 bits)
sizeof (struct s8) = 1 ( 8 bits)
sizeof (struct s16) = 2 (16 bits)
sizeof (struct s32) = 4 (32 bits)
sizeof (struct s64) = 8 (64 bits)
【问题讨论】:
-
灵感来自 this question 上 cmets 的讨论。
-
clang(至少我安装在我目前使用的 32 位机器上的那个)说
struct s64是 32 位,我的 gcc 在这里也是如此。 -
@DanielFischer clang 3.0、icc 13.1.1、tcc 0.9.25 和 gcc 4.7 产生了 Keith 发布的确切输出。 (在 amd64 盒子上)
-
@cnicutar 并不让我感到惊讶,正如我所说,我现在在一个 32 位的盒子上,32 位是这里的最大对齐要求。我认为基本原理是将指定的类型解释为对齐请求。因此,您可以获得您要求的对齐方式,或者如果您想要更多对齐方式,则可以达到最大值。
-
如果你用
-Wpadded编译,你会得到一些warning: padding struct size to alignment boundary [-Wpadded](clang还说添加了多少填充)。这似乎证实了编译器将指定的类型作为对齐提示。可能假设程序员通过声明一个大于必要的类型来知道他在做什么。