当使用CI进行编程时,发现使用GCC打包结构非常有价值__attribute__((__packed__))
[...]
既然您提到了__attribute__((__packed__))
,我假设您的意图是消除内的所有填充struct
(使每个成员具有1字节的对齐方式)。
没有适用于所有C编译器的打包结构的标准吗?
...答案是“否”。相对于结构(以及堆栈或堆中结构的连续数组)的填充和数据对齐是一个重要原因。在许多计算机上,未对齐的内存访问可能会导致潜在的显着性能下降(尽管在某些较新的硬件上变得更少)。在某些极少数情况下,未对齐的内存访问会导致不可恢复的总线错误(甚至可能使整个操作系统崩溃)。
由于C标准着重于可移植性,因此没有一种标准的方法来消除结构中的所有填充并仅允许任意字段未对齐是没有意义的,因为这样做可能会导致C代码不可移植。
以消除所有填充的方式将此类数据输出到外部源的最安全,最可移植的方法是串行化到字节流/从字节流串行化,而不仅仅是尝试传递您的原始内存内容structs
。这还可以防止您的程序在此序列化上下文之外遭受性能损失,并且还可以让您自由地将新字段添加到中,struct
而不会影响整个软件。它还将为您提供一些空间来处理字节序以及类似的事情(如果有的话)。
有一种消除所有填充而不达到编译器特定指令的方法,尽管它仅适用于字段之间的相对顺序无关紧要的情况。给定这样的东西:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
...我们需要相对于包含这些字段的结构的地址来对齐内存访问的填充,如下所示:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
...其中.
表示填充。每个组件都x
必须与8字节边界对齐以提高性能(有时甚至是正确的行为)。
您可以通过使用SoA(数组结构)表示形式以一种可移植的方式消除填充(假设我们需要8个Foo
实例):
struct Foos
{
double x[8];
char y[8];
};
我们已经有效地拆除了该结构。在这种情况下,内存表示如下所示:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... 还有这个:
01234567
yyyyyyyy
...不再增加填充开销,并且不会涉及未对齐的内存访问,因为我们不再将这些数据字段作为结构地址的偏移量访问,而是作为有效数组的基地址的偏移量访问。
这还带来了以下优点:由于要消耗较少的数据(混合中不再有无关紧要的填充来减慢计算机的相关数据消耗速率),因此顺序访问速度更快,并且还可能使编译器非常琐碎地向量化处理。
缺点是它是PITA编码。随机访问的效率可能较低,因为在字段之间跨度较大时,AoS或AoSoA代表通常会做得更好。但这是消除填充并尽可能紧密包装而不用拧紧所有部件对齐方式的一种标准方法。