【问题标题】:Memory layout differences in structs结构中的内存布局差异
【发布时间】:2018-08-30 15:06:27
【问题描述】:

我在 C++ 中有以下结构

struct A {
  int a;
  double b;
  float c;
}

这个结构和添加了函数的结构在内存布局上有区别吗?

struct B {
  int a;
  double b;
  float c;
  void foo();
}
B::foo() { //do stuff }

【问题讨论】:

  • 简单到可以弄清楚:printf("A: %zx, B: %zx\n", sizeof(struct A), sizeof(struct B))
  • XY 问题?为什么要了解内存布局?
  • @n.m.这很愚蠢。因为他有。
  • @n.m.我个人在嵌入式系统中工作,由于有限的内存和缓存优化,内存布局(特别是结构大小)可能非常重要,所以我认为这是一个合理的问题。 (是的,我使用 C++ 进行嵌入式)
  • @n.m.你知道这对于 C++ 来说完全是垃圾,因为它不像你所说的那样高级。我说您的评论很愚蠢的原因是因为我可以询问同一编译单元中的静态初始化顺序,这是最重要的了解,但似乎您的 XY 评论也将针对该问题。了解在这种语言中内存是如何布局的很重要。

标签: c++ memory struct


【解决方案1】:

这个结构和添加了函数的结构在内存布局上有区别吗?

可能会。

由于AB 是标准布局1,并且它们共同的初始序列由每个非静态数据成员2组成,因此它们是布局兼容

标准只描述抽象机器的语义,因此没有保证A 类型的对象将在内存中表示为B 类型的对象,但是布局兼容类型往往是。


1)[class]/7

标准布局类是这样的类:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  • 没有虚函数 (10.3) 和虚基类 (10.1),
  • 对所有非静态数据成员具有相同的访问控制(第 11 条),
  • 没有非标准布局基类, 要么在派生最多的类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且
  • 没有与第一个非静态数据成员相同类型的基类。

2)[class.mem]/21 & [class.mem]/22

如果两个标准布局结构(第 9 条)类型具有相同数量的非静态数据成员并且相应的非静态数据成员(按声明顺序)具有布局兼容类型 (3.9),则它们是布局兼容的。

【讨论】:

  • 该标准实际上并没有说明“布局兼容”的含义,只是指向此类类型的指针必须具有相同的表示形式。或者我什么都找不到。
  • @n.m.我不确定是什么促使您发表评论。我并不是说标准对布局兼容类型有任何要求,恰恰相反。
  • 很抱歉,我只想说清楚,仅此而已。没看到你也这么说。
  • @n.m.好的,我很好:显式>隐式。
  • @haelix 因为他们提供了一些保证。比如[class.mem]/25
【解决方案2】:

是,不是……

在您的具体情况下,没有。 struct 只不过是一个数据容器,而函数驻留在其他地方。调用函数时,指向结构的指针作为附加的隐式第一个参数传递,在函数中显示为this 指针。

不过,如果你添加一个 virtual 函数,事情就会发生变化。尽管 C++ 标准没有强制要求,但 vtables 是 defacto 标准,并且该类将接收指向 vtable 的指针作为第一个但不可见的成员。您可以通过在添加虚函数之前和之后打印出对象的大小来尝试一下。

另一方面,如果类虚拟的,因为继承自另一个已经有虚拟函数的类,内存布局不会再改变,因为已经有一个指向包括 vtable(尽管它会指向子类实例的不同位置)。

【讨论】:

    【解决方案3】:

    C++ 标准保证 C 结构和 C++ 类(或结构——相同的东西)的内存布局是相同的,前提是 C++ 类/结构符合 POD(“普通旧数据”)的标准.那么POD是什么意思呢?

    如果满足以下条件,则类或结构是 POD:

    所有数据成员都是公共的,它们本身是 POD 或基本类型(但不是引用或指向成员的类型),或此类的数组

    • 它没有用户定义的构造函数、赋值运算符或析构函数
    • 没有虚函数
    • 它没有基类

    所以在你的情况下是的,内存布局是一样的。

    来源:Structure of a C++ Object in Memory Vs a Struct

    【讨论】:

    • 概念命名为StandardLayoutType,见std::is_standard_layout
    • 这涵盖了 POD(或现在已知的标准布局类型),但我不确定 OP 是否打算将他们的问题限制在他们身上。例如,成员 std::string 不会合理地破坏该布局保证,即使该类型不再是标准布局。
    • 标准布局是我在POD之后学到的一个术语,它们是等价的还是有细微差别的?
    • 哇,刚发现有std::is_standard_layout和std::is_pod,我最好看看这个。
    • 从 POD 到标准布局的更改现在需要 class A { private: int one; float two; } 匹配 struct B { public: int one; float two; }。 POD 要求所有内容都公开
    【解决方案4】:

    这在形式上取决于您的编译器,但是声明非虚拟成员函数会更改类布局的编译器将是临界破坏。您需要这种稳定性来强制共享对象依赖于每个平台的兼容性。

    【讨论】:

    • A 和 B 布局不兼容吗?
    • @YSC 请不要再深入到标准的这一部分... 叹息。我马上回来。
    猜你喜欢
    • 2012-12-31
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 2015-01-12
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多