例如,请考虑以下事项:
假设int是4字节对齐的,而long是8字节对齐的。
struct example
{
int a;
long b;
int c;
};
编译器在内存中放置它的明显方法是: AAAAPPPPBBBBBBBCCCCPPPP,整个结构具有8字节对齐。
在这种情况下,sizeof(示例)为24。
但另一种方法是: AAAABBBBBBBBCCCC与整个结构有对齐,使得起始字节mod 8的地址= 4(不确定怎么说这个更简洁)
在这种情况下,不需要填充,因此每个实例可以节省8个字节。
我的问题是,编译器是否允许这样做(按标准)?他们真的这样做了吗?我总是看到对齐仅以字节为单位进行讨论。
答案 0 :(得分:2)
结构不能具有不比其成员的对齐要求严格的对齐要求。如果结构的成员是8字节对齐的,则结构需要至少8字节对齐。如果结构是8字节对齐的,那么在您的示例中,第二个成员不会是8字节对齐,因为它从8字节对齐的结构的开头偏移了四个字节,因此它不符合要求
可能的替代方法是将padding放在struct的开头,但不允许这样做:
C ++ 03 9.2p17
指向POD结构对象的指针(适当地使用
reinterpret_cast
转换)指向其初始成员(...),反之亦然。 [注意:因此,在POD结构对象中可能存在未命名的填充,但不是在其开头,以实现正确的对齐。]
另一种可能的替代方案是(正如你的建议)让8字节对齐实际意味着((address%8)==4)
(而不是(address%8)==0
)。如果是这种情况,那么你的8字节对齐long
具有相同的要求。不可能同时具有(address%8)==0
和(address%8)==4
对齐的类型,因为没有办法一般地分配满足两者的内存对齐要求。由于long
也会有这种特殊的对齐要求,你仍然无法避免填充。
答案 1 :(得分:2)
你忘记了数组。在一般情况下,你如何建议编译器应该确保每个数组元素在数组的情况下“起始字节mod 8 = 4”?
请记住,C语言要求对于T a[N]
类型的任何数组对象,以下都适用
sizeof a == sizeof *a * N
这意味着整个数组对象a
中存在的任何填充必须来自T
类型的单个元素。不允许数组添加自己的填充。
换句话说,编译器不能简单地记住正确对齐各个struct对象。它实际上必须将填充包含在struct对象中,并将该填充计为该结构对象的sizeof
的一部分。
你的AAAABBBBBBBBCCCC
是一个“好”的,因为当这些对象紧凑地存储在一个数组中时(没有额外的填充),如果数组的第一个元素正确对齐,它们都会正确对齐。但是,如果数组内存由malloc
分配,那么您期望如何实现第一个元素的正确对齐? malloc
对分配内存的类型一无所知,这意味着它无法根据您的类型的特定对齐要求定制其行为。
答案 2 :(得分:0)
编译器将尝试通过添加填充来对齐每个成员。通常存在最大对齐,这取决于编译器和处理器(例如,通常您没有像256字节对齐那样的对齐)。这个最大对齐甚至不存在于标准中,因此编译器对齐大小为2048的结构并非不可能......
正如DyP在评论中所说,gcc可以将其打包为:
AAAA PPPP BBBB BBBB CCCC PPPP
请注意,它可以采用其他方式打包(在Ideone上使用double
进行试用并与Stacked-Crooked进行比较)。
如果您不想填充,可以使用预处理器指令:
#pragma pack
有了这个,用于前一个示例的相同版本的gcc将其打包为:
AAAA BBBB BBBB CCCC