为什么编译器会抱怨对齐?

时间:2012-09-19 19:15:22

标签: c++ visual-c++ compiler-construction alignment compiler-warnings

我想更多地了解对齐方式。为什么Microsoft编译器(Visual Studio 2012 Express)会抱怨以下代码片段的对齐?

__declspec(align(16)) class Foo
{
public:
    virtual ~Foo() {}
    virtual void bar() = 0;
};

这是编译器向我提出的警告:

warning C4324: 'Foo' : structure was padded due to __declspec(align())

该类是否具有任何虚拟方法甚至无关紧要。即使对于空类,编译器也会使用相同的警告消息进行抱怨。空类是如何对齐的?编译器如何填充此类?

4 个答案:

答案 0 :(得分:16)

警告并不一定意味着您做错了什么,但告诉您可能没有打算这样做。请注意,允许编译器警告开发人员认为值得警告的任何内容。原则上,你也可以在13日星期五被警告编译。

在这种特定情况下,假设可能是当您指定对齐时,您不希望使类更大。因此,如果由于你给出的对齐要求而使类变得更大,那么你就不会犯错误。

当然,这就留下了为什么对齐要求使类更大的问题。现在我们回到标准领域(尽管__declspec本身是Microsoft扩展而不是标准)。 C ++标准要求在数组中,对象彼此跟随,其间没有任何空间。因此,如果对象必须与16字节边界对齐,则对象的大小必须是16的倍数。如果成员的大小(显式和隐式)都没有给出必要的大小,则编译器必须将未使用的字节添加到对象。这些字节称为填充。请注意,即使在不是数组成员的对象中也存在此填充。

现在你的类只包含一个隐式虚拟指针(因为它包含虚函数),根据体系结构,它可能是4或8字节大。由于您已请求16字节对齐,因此编译器必须添加12或8字节的填充以使大小为16的倍数,如果没有手动对齐规范,则不必添加。这就是编译器警告的内容。

答案 1 :(得分:3)

在x86中,Foo需要4个字节,因此需要一个12字节的填充。在x64中,Foo需要8个字节,因此需要一个8字节的填充。

答案 2 :(得分:2)

这个警告说的是,由于使用sizeof,类的大小(由__declspec(align())返回)已经改变(增加)。这可能会破坏事情,从而发出警告。

当然,空类的大小必须大于0,因此它至少为1.

通常大小不会随着对齐而改变,但在你的情况下确实如此,因为你指定的对齐方式大于类的无衬垫大小。请记住,在C中,类型的对齐不能小于其大小,实际上大小必须是对齐的倍数,因此增加大小以匹配对齐。

为什么尺寸必须是对齐的倍数?好吧,想象一下这种类型的数组:连续元素需要由sizeof(T)精确分隔,但每个对象必须在多个对齐的内存地址中。这个等式的唯一解决方案是sizeof(T)必须是对齐的(非空)倍数。

答案 3 :(得分:1)

使用时增加结构的对齐方式  __declspec(align())alignas(),它不仅会导致您的结构更严格地对齐,而且可能必须填充您的结构,根据这个rule

  

结构的大小是其对齐的最小倍数,大于或等于其最后一个成员的末尾的偏移量。

换句话说,如果你将结构的对齐增加到16,你最好确保你的结构的大小也是16的倍数,否则你的结构可能因填充而改变大小,这可能是导致您的程序中断(编译器无法判断您依赖结构的大小以及是否了解此规则,因此会发出警告)。