在C中,有时结构的某些成员倾向于具有未对齐的偏移量,如HPUX community中的此线程
在这种情况下,建议使用零宽度位字段来对齐(未对齐的)下一个成员。 在什么情况下结构成员的错位发生?在字边界处对齐成员偏移不是编译器的工作吗?
答案 0 :(得分:4)
packed
属性`。)
例如,在引用的问题中,问题是存在结构:
struct {
// ... stuff
int val;
unsigned char data[DATA_SIZE];
// ... more stuff
}
并且程序员尝试使用data
,就好像它是size_t
:
*(size_t*)s->data
但是,程序员已将data
声明为unsigned char
,因此编译器仅保证将其对齐以用作unsigned char
。
碰巧,data
跟在int
之后,因此也会与int
对齐。在某些体系结构上,这可行,但在目标体系结构上,size_t
大于int
并且需要更严格的对齐。
显然,编译器无法知道您打算使用结构成员,就像它是其他类型一样。如果你这样做并编译一个需要正确对齐的架构,你可能会遇到问题。
引用的线程建议在声明size_t
数组之前插入一个零长度unsigned char
位字段,以强制数组与size_t
对齐。虽然该解决方案可能适用于目标体系结构,但它不可移植,不应在便携式代码中使用。不能保证0长度的比特字段将占用0比特,也不能保证基于size_t
的比特字段实际存储在size_t
中或者被适当地对齐任何非比特字段使用。
更好的解决方案是使用匿名联盟:
// ...
int val;
union {
size_t dummy;
unsigned char data[DATA_SIZE];
};
// ...
使用C11,您可以明确指定最小对齐:
// ...
int val;
_Alignas(size_t) unsigned char data[DATA_SIZE];
// ...
在这种情况下,如果你#include <stdalign.h>
,你可以用一种也适用于C ++ 11的方式拼写_Alignas
:
int val;
alignas(size_t) unsigned char data[DATA_SIZE];
答案 1 :(得分:1)
您可能已经意识到结构字段与特定边界对齐的原因是为了提高性能。正确对齐的字段可能仅需要CPU的单个存储器提取操作;错误对齐的字段需要至少两次内存提取操作(CPU时间的两倍)。
正如您所指出的,编译器工作是将结构字段对齐以获得最快的CPU访问;除非程序员超越编译器的默认行为。
然后问题可能是;为什么程序员会忽略编译器结构字段的默认对齐?
程序员想要覆盖默认路线的原因的一个例子是在线路上发送一个结构&#39;到另一台电脑。通常,程序员希望将尽可能多的数据打包到最少的字节数。
因此,当结构密度比访问结构字段的CPU性能更重要时,程序员将禁用默认对齐。