【问题标题】:MSVC Object Layout QuirkMSVC 对象布局怪癖
【发布时间】:2011-01-16 02:15:56
【问题描述】:

我在 C++ 中有一个简单的类,它有一个整数和一个 vtable:

class Something {

   virtual void sampleVirtualMethod();

   int someInteger;
};

如果您查看 MSVC 的对象布局(使用 /d1reportSingleClassLayout),您会得到:

class Something       size(8):
        +---
 0      | {vfptr}
 4      | someInteger
        +---

这完全有道理。 4 个字节用于 vtable 指针,4 个字节用于整数。奇怪的是当我在类中添加一个 double 时:

class Something {    
    virtual void sampleVirtualMethod();
    int someInteger;
    **double someDouble;**
};

我得到了这个对象布局:

class Something       size(24):
        +---
 0      | {vfptr}
 8      | someInteger
        | <alignment member> (size=4)
16      | someDouble
        +---

为什么偏移量 0 和 someInteger 的区别是 8 而不是 4? vtable 是否以某种方式增长到 8 个字节?无论我添加双精度的顺序如何,都会发生这种情况。

谢谢。

【问题讨论】:

  • 我不禁想知道你为什么会关心这个。这会对您的工作造成直接问题吗?
  • 为什么关心它? OP(和我)感到惊讶。尝试学习新事物有什么坏处? ;)
  • 我正在尝试在二进制文件和 Visual Studio 二进制文件之间合并对象模型。这实际上是一个问题:)。奇怪的是,GCC 和 Visual Studio 都像这样布置对象,而 llvm 没有。
  • 在现实生活中的程序中,如果对象被实例化了很多次,我会非常关心这个。浪费的内存影响将是毁灭性的
  • 这里的问题是vfptr的size不可能改成8,还是4。但是由于int成员的偏移量改成了8,所以肯定有&lt;alignment member&gt;的它们之间的尺寸为 4。但是,报告中没有任何内容。这没有任何意义。看起来像一个错误。

标签: c++ layout object virtual compiler-construction


【解决方案1】:

我无法直接回答您的问题,因为编译器的行为没有很好的借口。相反,我会沉迷于疯狂的猜测,因为还没有答案。

我怀疑对齐算法中存在如下错误:

  • 结构的对齐方式(即第一个成员的位置)应至少与其最宽成员的对齐方式一样宽
  • 糟糕,忘记将虚函数表指针计为成员

如果存在此错误,我怀疑它是编译器早期作为 4 字节对齐的 C 编译器遗留下来的。现在编译器的默认值是/Zp8,这意味着每个结构至少对齐到 8 个字节,因此在这种情况下无论如何都不需要修复“first”成员的对齐方式。

问候, 舍姆

【讨论】:

    【解决方案2】:

    我怀疑this answer 与此有关。引用dirkgently的回答,引用GCC手册:

    请注意,ISO C 标准要求任何给定结构或联合类型的对齐方式至少是相关结构或联合的所有成员的最小公倍数的完美倍数。

    根据该规则,添加 8 字节双精度后,编译器必须将所有内容排列在 8 字节倍数上。当然,您可以使用#pragma pack() 覆盖它,尽管如果您最终在非 8 字节边界上使用 8 字节成员,则效率会降低。

    【讨论】:

    • 不,它没有。引用的唯一内容是,由于 double 成员 整个结构 应该在 8 字节边界处对齐。但它没有解释为什么整数成员的对齐方式发生了变化。这个结构应该适合 16 个字节,并且完全满足所有对齐要求。但是编译器生成了 24。为什么?
    • 有趣的是,VC++ 8 中 x64 代码的对齐示例页面显示了相同的内容,其中包含一个 int 和一个 double 的结构,但遗憾的是没有说明原因。 msdn.microsoft.com/en-us/library/71kf49f1(VS.80).aspx 显然,编译器选择在最小公分母上对齐成员,即 8,因此需要对齐。这句话可能有解释的余地​​。
    • aaplem - 该链接使用 int 和 double 显示相同的内容。这可能是因为您希望双精度为 8 字节对齐。我一直认为 4 字节对齐的双精度在 x86 上非常慢。
    • @Mason - 同意;我绝对希望我的双打是 8 字节对齐的!我只是想知道数据成员的对齐是否独立于 vtbl 指针进行处理。 MS 的文档讨论了数据成员对齐,但我没有找到任何地方表明 vtbl 是否被视为这些成员之一。自从我从事一个迫使我考虑 vtbl 布局的项目以来已经有 10 年了,所以我已经忘记了我曾经知道的大部分内容:-)(那是针对 VC++ 4.2,无论如何,现在可能不同了) .
    【解决方案3】:

    This blog post 讨论了同样的问题,并在 cmets 中包含了 Jan Gray 的解释,他很久以前就编写了布局代码 MS C++ 编译器。

    简单地说,vfptr 仅在其他数据成员布局后才插入到类布局中。根据数据成员的对齐要求,这意味着可能会引入不必要的填充。此外,仅当该类没有具有 vfptr 的基类时才会发生这种情况。

    因此,在博客文章中介绍了解决方法:

    class EmptyBase
    {
    protected:
        virtual ~EmptyBase() {}
    };
    
    class Something : public EmptyBase {    
        virtual void sampleVirtualMethod();
        int someInteger;
        **double someDouble;**
    };
    

    sizeof(Something) 在这种情况下应该是 16。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      相关资源
      最近更新 更多