我想更多地了解对齐方式。为什么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())
该类是否具有任何虚拟方法甚至无关紧要。即使对于空类,编译器也会使用相同的警告消息进行抱怨。空类是如何对齐的?编译器如何填充此类?
答案 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的倍数,否则你的结构可能因填充而改变大小,这可能是导致您的程序中断(编译器无法判断您依赖结构的大小以及是否了解此规则,因此会发出警告)。