这些是位字段。 “预期”输出没有太多影响,因为标准对这些输出的规定非常糟糕。此外,编译器往往对它们的支持很差。
首先,您引用的完整部分(6.7.2.1/11)说:
实现可以分配任何大的可寻址存储单元
足以容纳一个位域。如果还有足够的空间,则一个位字段
紧随结构中另一个位字段之后的位字段应被打包
到同一单元的相邻位。如果剩余空间不足,
是否将不适合的位域放入下一个单元或
重叠相邻单元是实现定义的。的顺序
单元内位域的分配(高位到低位或
从低阶到高阶)是实现定义的。的对齐
可寻址存储单元未指定。
所有这些意味着您几乎无法对这些位最终如何进入内存做出任何假设。你无法知道编译器将如何安排对齐,你无法知道位的位顺序,你无法知道符号,你无法知道字节序。
至于如果int
and _Bool
将合并到同一个“存储单元”中......那么,他们为什么会这样做呢?它们是不兼容的类型。 C 标准没有提到当您有两个不兼容类型的相邻位字段时会发生什么 - 它对主观解释持开放态度。我怀疑会有各种类型别名问题。
因此,编译器将所有这些放在单独的“存储单元”中是完全可以的。如果您将相同类型的项目相邻放置,我希望它能够合并它们,否则引用的部分将没有任何意义。例如,我期望以下尺寸为 8:
struct s {
int a : 6;
int h : 12;
_Bool b : 1;
_Bool c : 1;
_Bool d : 1;
_Bool e : 1;
_Bool f : 1;
_Bool g : 1;
};
现在,如果您想要确定性的可移植代码,您应该做的就是将位字段扔出窗口并使用按位运算符。它们是 100% 确定性且可移植的。
假设您实际上不想要一些神秘的带符号数字字段(您的原始代码暗示了这一点),那么:
#define a UINT32_C(0xFC000000)
#define b (1u << 18)
#define c (1u << 17)
#define d (1u << 16)
#define e (1u << 15)
#define f (1u << 14)
#define g (1u << 13)
#define h UINT32_C(0x00000FFF)
typedef uint32_t special_thing;
然后设计 setter/getter 函数或宏来设置/获取此 32 位块中的数据。如果编写得当,您甚至可以使此类代码与字节序无关。