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