首页  >  文章  >  后端开发  >  C 如何在内存中排列对象,这对于动态转换和重新解释意味着什么?

C 如何在内存中排列对象,这对于动态转换和重新解释意味着什么?

DDD
DDD原创
2024-11-02 03:52:30307浏览

How does C   arrange objects in memory, and what does it mean for dynamic casting and reinterpreting?

C 对象的内存布局

问题:

C 在内存中是如何布置对象的?听说动态强制转换只调整对象在内存中的指针,带偏移量;而重新解释允许我们对此指针做任何操作。我不太理解。请提供详细信息!

解答:

内存布局

内存布局主要取决于实现。但有一个关键例外,即具有相同访问说明符的成员变量将按声明顺序排列。

§ 9.2.14

非静态数据成员(非联合)具有相同访问控制权的类(Clause 11)被分配,以便后续成员在类对象中具有更高的地址。具有不同访问控制权的非静态数据成员的分配顺序未指定(11)。实现对齐要求可能导致两个相邻成员不会立即相互分配;管理虚拟函数(10.3)和虚拟基类(10.1)的空间要求也可能导致这种情况。

其他内存管理

除了成员变量之外,类或结构还需要为成员变量、基类的子对象、虚拟函数管理(例如虚拟表)以及这些数据的填充和对齐提供空间。这取决于实现,但 Itanium ABI 规范是一种流行的选择。gcc 和 clang 遵循它(至少在一定程度上)。

http://mentorembedded.github.io/cxx-abi/abi.html#layout

Itanium ABI

当然,Itanium ABI 不是 C 标准的一部分,也没有约束力。要获得更详细的信息,你需要查阅实现者的文档和工具。clang 提供了一个工具来查看类的内存布局。例如,以下内容:

class VBase {
    virtual void corge();
    int j;
};

class SBase1 {
    virtual void grault();
    int k;
};

class SBase2 {
    virtual void grault();
    int k;
};

class SBase3 {
    void grault();
    int k;
};

class Class : public SBase1, SBase2, SBase3, virtual VBase {
public:
    void bar();
    virtual void baz();
    // 不允许虚拟成员函数模板,原因考虑内存布局和虚表
    // template<typename T>
    // virtual void quux();
private:
    int i;
    char c;
public:
    float f;
private:
    double d;
public:
    short s;
};

class Derived : public Class {
    virtual void qux();
};

int main() {
    return sizeof(Derived);
}

在创建使用类内存布局的源文件后,clang 将显示内存布局。

$ clang -cc1 -fdump-record-layouts layout.cpp

Class 的布局:

*** Dumping AST Record Layout
   0 | class Class
   0 |   class SBase1 (primary base)
   0 |     (SBase1 vtable pointer)
   8 |     int k
  16 |   class SBase2 (base)
  16 |     (SBase2 vtable pointer)
  24 |     int k
  28 |   class SBase3 (base)
  28 |     int k
  32 |   int i
  36 |   char c
  40 |   float f
  48 |   double d
  56 |   short s
  64 |   class VBase (virtual base)
  64 |     (VBase vtable pointer)
  72 |     int j
     | [sizeof=80, dsize=76, align=8
     |  nvsize=58, nvalign=8]

更多内存布局信息

更多有关此 clang 特性的信息可以在 Eli Bendersky 的博客上找到:

http://eli.thegreenplace.net/2012/12/17/dumping-a-c-objects-memory-layout-with-clang/

gcc 提供了一个类似的工具 `-fdump-class-hierarchy'。对于上面给出的类,它输出(除其他内容外):

Class Class
   size=80 align=8
   base size=58 base align=8
Class (0x0x141f81280) 0
    vptridx=0u vptr=((&amp; Class::_ZTV5Class) + 24u)
  SBase1 (0x0x141f78840) 0
      primary-for Class (0x0x141f81280)
  SBase2 (0x0x141f788a0) 16
      vptr=((&amp; Class::_ZTV5Class) + 56u)
  SBase3 (0x0x141f78900) 28
  VBase (0x0x141f78960) 64 virtual
      vptridx=8u vbaseoffset=-24 vptr=((&amp; Class::_ZTV5Class) + 88u)

它没有逐项列出成员变量(或者至少我不知道如何获取),但你可以看出它们必须位于偏移量 28 和 64 之间,就像 clang 布局中一样。

你可以看到一个基类被指定为 primary。当 Class 被作为 SBase1 访问时,这取消了 this 指针调整的需要。

其他编译器指令

以下等价指令适用于不同编译器:

  • gcc:$ g -fdump-class-hierarchy -c layout.cpp
  • Visual C :cl main.cpp /c /d1reportSingleClassLayoutTest_A

见:https://blogs.msdn.microsoft.com/vcblog/2007/05/17/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022/

以上是C 如何在内存中排列对象,这对于动态转换和重新解释意味着什么?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn