一个类型是否可能有除了“n字节对齐”以外的对齐要求?

7
例如,考虑以下内容:
假设int是4个字节对齐的,而long是8个字节对齐的。
struct example
{
    int a;
    long b;
    int c;
};

编译器在内存中布局的显而易见的方式是:AAAAPPPPBBBBBBBBCCCCPPPP,整个结构有8字节对齐。
P代表填充字节,A代表a的一个字节,B代表b的一个字节,C代表c的一个字节。
在这种情况下,example的sizeof为24。
但另一种方法是这样做的:
AAAABBBBBBBBCCCC,整个结构的对齐方式是起始字节地址模8 = 4(不确定如何更简洁地说)。
在这种情况下,不需要填充,因此每个实例可节省8个字节。
我的问题是,编译器是否允许这样做(按照标准)?他们真的这样做吗?我总是看到对齐只是以字节为单位讨论。

5
标准布局结构体的开头可能没有填充,参见[class.mem]/19。其中一个原因是这样做可以将指向结构体的指针转换为指向其第一个成员的指针。 - dyp
3
@Mat 这是原帖问题的核心——这个结构体的对齐需求是否可以是"必须对齐在8字节的后半段。" - Angew is no longer proud of SO
@DyP 是的,你说得对,我已经改过了,但这并不真正影响问题。 - Bwmat
@hvd 数组元素之间不会有填充。 - Bwmat
1
@Bwmat 你说得对,我误解了你的建议。一个可能会出现问题的地方是让内存分配函数返回一个适当对齐的指针。它们不知道它们正在为哪种类型进行分配。另一个问题是将你的结构体放在联合体中与一个 long 并列。联合体类型需要同时满足8+0和8+4的对齐要求。 - user743382
显示剩余5条评论
3个回答

2
结构体的对齐要求不能低于其成员的对齐要求。如果结构体的成员需要8字节对齐,则该结构体至少需要8字节对齐。在你的示例中,如果结构体需要8字节对齐,则第二个成员将不会8字节对齐,因为它距离8字节对齐结构体的开头偏移了4个字节,所以它无法满足要求。
可能的替代方案是在结构体开头放置填充,但这是不允许的:
C++03 9.2p17
指向POD结构体对象的指针,使用reinterpret_cast适当地转换后,指向其初始成员(...),反之亦然。[注意:因此,在POD结构体对象内部可能有未命名的填充,但不能在其开头,因为需要实现正确的对齐。]
另一个可能的替代方案是(正如您所建议的那样)使8字节对齐实际上意味着(address%8)==4(而不是(address%8)==0)。如果是这样的话,那么你的8字节对齐的long类型也会有同样的要求。无法同时具有(address%8)==0和(address%8)==4对齐的类型,因为没有办法通用地分配满足两种对齐要求的内存。由于long类型也具有这种特殊的对齐要求,因此仍然无法避免填充。

你误解了,这个结构体不会是8字节对齐的。它将从8字节对齐偏移4字节。 - Bwmat
@Bwmat:一个结构体的对齐要求不能比其成员的对齐要求更宽松。 - Vaughn Cato
1
我不会说这个要求不严格。它同样严格,只是有所偏移。无论如何,有人能引用一下标准吗? - Bwmat
@Bwmat:我明白你的意思。我已经添加了更多内容到我的答案中,希望能解决这个问题。 - Vaughn Cato

2
你忘记了数组。在一般情况下,如果是数组元素的情况下,你如何建议编译器确保“起始字节模8=4”?
请记住,C语言要求对于任意类型为T、大小为N的数组对象a[N],以下内容必须成立。
sizeof a == sizeof *a * N

这意味着数组对象a内部存在的任何填充必须来自类型T的单个元素。数组不允许添加其自己的填充。
换句话说,编译器不能仅仅记住正确对齐单独的结构体对象。它实际上必须将填充包括在结构体对象中,并将该填充计入该结构体对象的sizeof的一部分。
您的AAAABBBBBBBBCCCC是“好”的,因为当这些对象以紧密排列在数组中(没有额外填充)时,如果数组的第一个元素正确对齐,则它们都会被正确对齐。但是,如果数组内存通过malloc进行分配,那么您如何期望为第一个元素实现正确对齐。例如,malloc不知道分配内存的类型,这意味着它无法根据您的类型特定对齐要求来调整其行为。

0

编译器会尝试通过添加填充来对齐每个成员。通常有一个最大对齐值,这取决于编译器和处理器(例如通常不会有256字节对齐之类的对齐方式)。这个最大对齐值甚至没有出现在标准中,因此编译器可以对大小为2048的结构体进行对齐...

正如DyP在评论中所说,gcc可以将其打包为:

AAAA PPPP BBBB BBBB CCCC PPPP

实时示例

请注意,它可以以另一种方式打包(在Ideone上尝试使用double并与Stacked-Crooked进行比较)。

如果您不想要填充,则可以使用预处理器指令:

#pragma pack

这与gccMSVC兼容。

使用此功能,与前一个示例中使用的相同版本的gcc打包如下:

AAAA BBBB BBBB CCCC

实时示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接