MSVC默认的内存对齐方式为8。

6
根据MSDN/Zp命令默认为8,这意味着使用64位对齐边界。我一直认为对于32位应用程序,MSVC编译器将使用32位边界。例如:
struct Test
{
   char foo;
   int bar;
};

编译器会像这样填充它:
struct Test
{
   char foo;
   char padding[3];
   int bar;
};

所以,既然默认使用/Zp8,那么按照上面的例子,我的填充将变为7 + 4字节:
struct Test
{
   char foo;
   char padding1[7];
   int bar;
   char padding2[4];
}; // Structure has 16 bytes, ending on an 8-byte boundary

这有点荒谬,难道我误解了吗?为什么要使用如此大的填充,看起来很浪费空间。在32位系统上,大多数类型甚至不会使用64位,因此大多数变量都将具有填充(可能超过80%)。

你能解释一下 7+4 吗?我现在有点困惑这个问题。 - Mysticial
@Mysticial:我在我的原帖中更新了另一个示例,以便向您展示我的意思。对于造成的困惑,我感到抱歉。 - void.pointer
感谢澄清。我刚刚测试了你的第一个结构体,大小只有8个字节,通过第二个结构体片段进行填充。所以我无法重现你得到的结果。(或者至少不是你认为应该得到的结果) - Mysticial
2个回答

11

这不是它的工作原理。成员被对齐到其大小的倍数。char为1个字节,short为2个字节,int为4个字节,double为8个字节。在结构体的末尾填充以确保当结构体用于数组时,成员仍然正确对齐。

打包为8意味着它停止尝试对大于8的成员进行对齐。这是一个实际限制,内存分配器不会返回比8更好对齐的地址。如果double未正确对齐并且跨越缓存行,则操作非常昂贵。但如果编写SIMD代码,否则将会是一件头疼的事情,因为需要16字节对齐。


那么因为根据MSDN的默认对齐方式是8,那么我在帖子中最后的代码片段就是对齐填充的样子了吗?如果不是,请解释一下。 - void.pointer
1
@Robert:你最后的代码是错误的。它将像这样填充 char foo; pad[3] ; int bar;,因为 int 是结构体中最大的成员。你应该非常仔细地阅读汉斯所说的话。 - Benjamin

3

这并不意味着每个成员都对齐在8字节边界上。请仔细阅读:

the smaller member type or n-byte boundaries

关键在于第一部分,“较小的成员类型”。这意味着具有较少对齐要求的成员可能会被对齐得更少,从而有效地减小了对齐。
struct x {
    char c;
    int y;
};
std::cout << sizeof(x);
std::cout << "offsetof(x, c) = " << offsetof(x, c) << '\n';
std::cout << "offsetof(x, c) = " << offsetof(x, y) << '\n';

这将得到8、0、4三个数字,这意味着实际上int只是填充到4字节对齐。

1
你的回答最后一部分是不正确的。从结构中删除int并注意sizeof()不返回8。 - Hans Passant

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