A bit-field shall have a type that is a qualified or unqualified
version of_Bool
,signed int
,unsigned int
,or some other
implementation-defined type. It is implementation-defined whether
atomic types are permitted.
…
A bit-field is interpreted as having a signed or unsigned integer type
consisting of the specified number of bits. If the value 0 or 1 is
stored into a nonzero-width bit-field of type_Bool
,the value of
the bit-field shall compare equal to the value stored; a_Bool
bit-field has the semantics of a_Bool
.An implementation may allocate any addressable storage unit large
enough to hold a bit- field. If enough space remains,a bit-field that
immediately follows another bit-field in a structure shall be packed
into adjacent bits of the same unit. If insufficient space remains,
whether a bit-field that does not fit is put into the next unit or
overlaps adjacent units is implementation-defined. The order of
allocation of bit-fields within a unit (high-order to low-order or
low-order to high-order) is implementation-defined. The alignment of
the addressable storage unit is unspecified.
对于任何结构类型,类型的对齐方式至少是该类型的任何成员的最大对齐方式,并且任何类型的大小都是其对齐方式的倍数.例如,如果结构包含(非位域)int成员,并且int需要4字节对齐,则结构本身需要4字节对齐或更多.
许多编译器允许除_Bool和int类型之外的整数类型的位字段.
对于至少一些编译器,包含位字段的结构的对齐至少是声明的位字段类型的对齐.例如,对于x86_64上的gcc 4.7.2,给出:
struct sb { _Bool bf:1; }; struct si { unsigned bf:1; };
gcc给struct sb一个大小和1个字节的对齐(这是_Bool的大小和对齐),struct struct是一个大小和4个字节的对齐(这是int的大小和对齐).它对实现定义类型的位字段做了同样的事情;位字段定义为long long bf:1;强制封闭结构的8字节大小和对齐.即使在两种情况下,位字段bf是宽度仅为1位的对象,也可以完成此操作.
我在SPARC / Solaris 9上看到了与Sun编译器类似的行为.
实验表明,定义为_Bool或无符号的多个位字段可以打包到单个字节内的相邻位(实际上是必需的),因此位字段本身没有严格的对齐要求.
我理解结构成员的布局主要是实现定义的,我不相信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)
解决方法
The alignment of the addressable storage unit is unspecified.
编译器可以选择任何对齐并遵守C标准,但这不是全部.
为了使用不同编译器编译的代码进行互操作,平台ABI必须指定这些细节.例如,Linux x86使用的SYS-V i386 ABI说:
Bit-fields obey the same size
and alignment rules as other structure and union members,with the following
additions: […]
- A bit-field must entirely reside in a storage unit appropriate for its declared
type.
然后,无论宽度如何,长位域必须驻留在4字节边界上对齐的内容中.