在文章 <35qdf0$7iq@netaxs.com> 中,Eric Raymond 说道:
> 每个
> 连续的位字段组的第一个成员通常是字对齐的,所有后续的
> 位字段会连续地打包到后续的字中(尽管 ANSI C 只要求后者
> 适用于小于字大小的位字段组)。
错误。ISO C(请注意名称的准确性)要求第一个位字段必须放入某个可寻址的对象中,但没有要求它必须是“字”(因此,在 32 位系统上,一个 11 位字段可能会放入一个半字中)。如果后续字段能够放入同一个对象中,那么它必须放在那里。如果不能,它可以跨越两个对象,或者放入一个新的对象中。因此,
int a : 11; int b : 2; int c : 11; int d : 7;
可以打包为
32-bit object: a, b, c, d
或者打包为
16-bit object: a, b 16-bit object: c 16-bit object: d
或者打包为
16-bit object: a, b, 3 bits of c 16-bit object: 8 bits of c, d
> Q: 在结构体或数组中,我如何确定一个类型的“真实大小”(包括尾部填充)?
> A: 将其表观大小向上舍入到其对齐方式的倍数。
在结构体中,
offsetof (structure, next field) - offsetof (structure, field)
或
sizeof (structure) - offsetof (structure, last field)
数组中没有填充。
> A: 将其表观大小向上舍入到其对齐方式的倍数。
不一定。填充可能取决于后面的内容。
> (假设是 32 位架构,除非另有说明,否则 long=4,int=4,short=2,char=1 均为常规 C 大小)
> (假设是 32 位架构,除非另有说明,否则 long=4,int=4,short=2,char=1 均为常规 C 大小)
> (假设是 32 位架构,除非另有说明,否则 long=4,int=4,short=2,char=1 均为常规 C 大小)
并假设指针大小为 4。
> (4) struct {char *; short; long;} 将在 short 之后有一个半字的填充
> short 之后有一个半字的填充
或者之前。在大端机上,在低位半字中访问该半字可能更容易。
> Q: 如何完全控制结构体的位级布局?
> A: 这就是位字段的用途。将所有成员声明为位字段。
> 结构体可能仍然有不可见的尾部填充(除非你的最后一个
> 位字段正好填满最后一个机器字),但在现代
> 架构上,这将消除所有其他填充,并让你对声明部分的位布局
> 完全控制(尽管访问时间会显著增加)。然而,ANSI 并不*保证*这种
> 良好的行为,所以要小心陷阱。
> 好的行为,所以要小心陷阱。
首先,当字段不能精确匹配时,可能存在填充。其次,你仍然不能保证每个“可寻址对象”内部位字段的顺序。
> 在一些非常老的 36 位字导向架构上,一些实现不佳的 C 编译器上,即使这样也可能不够;一个跨越
> 越过字边界的位字段可能会强制下一个位字段从
> 下一个字的开头开始,留下高达 35 位的不可见间隙。
> 下一个字的开头开始,留下高达 35 位的不可见间隙。
或者在现代架构上实现良好的编译器上,这些编译器选择不去费力地将位字段分割到字边界。
> 在这些机器上,你唯一的选择是将结构体声明为单个
> char 数组,并在 C 语言中自己进行字段访问。
而且你仍然不知道字内位的顺序相对于你正在遵循的外部格式。这无法以严格兼容的方式解决。
-- Clive D.W. Feather | Santa Cruz Operation | If you lie to the compiler, clive@sco.com | Croxley Centre | it will get its revenge. Phone: +44 1923 813541 | Hatters Lane, Watford | - Henry Spencer Fax: +44 1923 813811 | WD1 8YN, United Kingdom | <= NOTE: NEW PHONE NUMBERS