我想了解更多关于对齐的信息。为什么 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())
该类是否有虚方法甚至并不重要。即使对于空类,编译器也会发出相同的警告消息。空类如何对齐?编译器如何填充这个类?
警告并不一定意味着您做错了什么,而是告诉您“可能”不是有意这样做的。请注意,编译器可以对开发人员认为值得警告的任何内容发出警告。原则上,您也可能会在 13 号星期五进行编译时收到警告。 在这种特定情况下,假设可能是当您指定对齐时,您不想使类更大。 因此,如果类由于您给出的对齐要求而变得更大,那么您不太可能犯错误。
当然,这就留下了为什么对齐要求会使类更大的问题。现在我们回到了标准领域(尽管
__declspec
本身是 Microsoft 扩展而不是标准)。 C++ 标准要求在数组中,对象彼此相邻,中间没有任何空格。因此,如果您的对象必须与 16 字节边界对齐,则该对象的大小必须是 16 的倍数。如果成员的大小(显式和隐式)未给出必要的大小,则编译器必须将未使用的字节添加到对象中。 这些字节称为填充。请注意,即使在不是数组成员的对象中也存在此填充。
现在你的类只包含一个隐式虚拟指针(因为它包含虚拟函数),根据体系结构,该指针可能有 4 或 8 字节大。由于您请求了 16 字节对齐,因此编译器必须添加 12 或 8 字节填充以使大小达到 16 的倍数,如果没有手动对齐规范,则不必添加填充。这就是编译器警告的内容。
sizeof
,类的大小(由
__declspec(align())
返回)已更改(增加)。这可能会破坏东西,因此发出警告。空类当然有一个大小,并且必须大于 0,因此它至少为 1。
通常大小不会随着对齐方式而改变,但在您的情况下会改变,因为您指定的对齐方式大于类的未填充大小。请记住,在 C 中,类型的对齐方式不能小于其大小,实际上大小必须是对齐方式的倍数,因此大小会增加以匹配对齐方式。
为什么尺寸必须是对齐方式的倍数?好吧,想象一下这种类型的数组:连续的元素需要精确地用
sizeof(T)
分隔,但每个对象必须位于对齐的内存地址倍数中。该方程的唯一解决方案是
sizeof(T)
必须是对齐的(非空)倍数。__declspec(align())
或
alignas()
,它不仅会使你的结构对齐得更严格,而且根据这个规则:可能必须在最后填充你的结构 结构的大小是其对齐方式大于或等于其最后一个成员末尾的偏移量的最小倍数。
换句话说,如果您将结构的对齐方式增加到 16,那么您最好确保结构的大小也是 16 的倍数,否则您可能会面临由于填充而导致结构大小发生变化的风险,这可能会导致您的程序要中断(编译器无法判断您对结构大小的依赖程度以及您是否了解此规则,因此它会发出警告)。