首先,这是ISO C标准关于位字段的内容,引用了2011 ISO C标准的N1570草案,第6.7.2.1节:
位字段应具有合格或不合格的类型 版本
_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
的多个位字段可以打包到单个字节内的相邻位中(实际上是必需的),因此位字段本身没有严格对齐要求。
据我所知,struct成员的布局主要是实现定义的,我不相信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)
答案 0 :(得分:5)
在某种程度上,您已使用标准中的引文自行回答了问题:
未指定可寻址存储单元的对齐方式。
编译器可以选择任何对齐并遵守C标准,但这不是全部。
为了使用不同编译器编译的代码进行互操作,平台ABI必须指定这些细节。例如,Linux x86使用的SYS-V i386 ABI表示:
位字段遵循相同的大小 和对齐规则作为其他结构和联合成员,具有以下内容 补充:[...]
- 位字段必须完全驻留在适合其声明的存储单元中 类型。
然后,无论宽度如何,long
位域必须驻留在4字节边界上对齐的位置。