12

我想了解更多关于对齐的信息。为什么 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

4 回答 4

19

警告并不一定意味着你做错了什么,而是告诉你你可能不是有意的这种行为。请注意,允许编译器警告开发人员认为值得警告的任何事情。原则上,您也可能会在 13 日星期五收到有关编译的警告。

在这种特定情况下,假设可能是当您指定对齐方式时,您不想让类更大。因此,如果由于您提供的对齐要求而使班级变大,那么您犯错误的可能性不大。

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

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

于 2012-09-19T19:41:03.933 回答
2

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

于 2012-09-19T19:25:36.297 回答
2

这个警告说的是类的大小(由 . 返回sizeof)由于使用__declspec(align()). 这可能会破坏事情,因此会发出警告。

空类当然有大小,而且必须大于 0,所以它至少为 1。

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

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

于 2012-09-19T19:27:32.840 回答
2

当您使用 __declspec(align())or增加结构的对齐方式时alignas(),它不仅会使您的结构更加严格地对齐,而且可能必须在最后填充您的结构,根据以下规则

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

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

于 2016-08-31T10:09:10.773 回答