自动对象驻留在哪里(与演示一起)

where does an automatic object live in( with a demo )

本文关键字:一起 在哪里 对象      更新时间:2023-10-16

我想在c++的内存管理方面做一些研究,以及它的实现,如g++, vc++。

第一个问题是自动对象(局部对象)驻留在哪里?(内置类型,用户定义的类型,STL…)

我认为内置类型存储在堆栈中,这是在编译步骤中完成的。对于用户定义的类型是什么呢?我以前在某个地方看到STL数据类型总是在堆内存中。所以我写了一个小函数,用g++编译,用objdump反汇编,看看编译器到底做了什么。

#include <string>
void autovar(){
    std::string s;
}

,反汇编的结果如下:

00000000 <__Z7autovarv>:
   0:   55                      push   %ebp //push the old frame pointer 
   1:   89 e5                   mov    %esp,%ebp //ebp point to the old
   3:   83 ec 28                sub    $0x28,%esp//allocate stack space
   6:   8d 45 f4                lea    -0xc(%ebp),%eax//param or something??
   9:   89 04 24                mov    %eax,(%esp)
   c:   e8 00 00 00 00          call   11 <__Z7autovarv+0x11>
  11:   8d 45 f4                lea    -0xc(%ebp),%eax
  14:   89 04 24                mov    %eax,(%esp)
  17:   e8 00 00 00 00          call   1c <__Z7autovarv+0x1c>
  1c:   c9                      leave
  1d:   c3                      ret
  1e:   90                      nop
  1f:   90                      nop

所以我可以理解前三行,我需要一些帮助来理解其余的

感谢您的关注!

标准免责声明:的实现可能与完全不同,但大多数在x86或类似平台上,可能会大致如下所述。

当您定义具有自动存储持续时间的对象时,该对象本身将在堆栈上分配。因此,让我们考虑一个稍微简化的向量:

template <class T, class Allocator = std::allocator<T> >
class vector {
    T *data;
    size_t currently_used;
    size_t allocated;
public:
    // ...
};

因此,当我们分配vector时,该对象本身 (data指针,currently_usedallocated计数器的存储)被分配到堆栈上。

假设一个典型的32位机器,指针和size_ts都是32位,这意味着堆栈上有12个字节的空间。对于更简单的类型(例如,intlong)和,甚至对于vector这样的类型,我们期望在相当多的情况下看到在寄存器中分配局部变量。编译器根据(猜测)最可能使用的寄存器来选择分配哪个寄存器。在像SPARC或Itanium这样具有批次寄存器的机器上,我们可以期望大多数本地/auto变量都在寄存器中。在x86上,我们有足够少的寄存器,堆栈使用非常普遍(尽管x86-64的可用寄存器翻了一番,这有很大帮助)。

vector本身然后使用Allocator对象在其他地方(通常,但不一定是自由存储区)获取存储您关心的数据(即,在vector 中存储的元素)的存储空间。

查看您包含的代码的具体细节:在我看来,大多数其余代码都在调用std::string对象的构造函数和析构函数。不幸的是,您使用了可怕的AT&T语法,这使得它几乎无法阅读。

自动变量通常在堆栈上分配,但它们的实现当然可能使用其他内存。在标准容器的情况下,它们将使用动态内存分配或Allocator用户提供的任何内存。所以,在这个例子中,s对象位于堆栈上,但它的数据可能在其他地方(如果它不是空的)。

程序集注释:

00000000 <__Z7autovarv>:
   0:   55                      push   %ebp //push the old frame pointer 
   1:   89 e5                   mov    %esp,%ebp //ebp point to the old
   3:   83 ec 28                sub    $0x28,%esp //allocate stack space
   6:   8d 45 f4                lea    -0xc(%ebp),%eax
        //store address of `s` (which is 12 bytes below %ebp) in eax
   9:   89 04 24                mov    %eax,(%esp) //put it on a stack (argument)
   c:   e8 00 00 00 00          call   11 <__Z7autovarv+0x11> //call constructor
  11:   8d 45 f4                lea    -0xc(%ebp),%eax
  14:   89 04 24                mov    %eax,(%esp)
  17:   e8 00 00 00 00          call   1c <__Z7autovarv+0x1c> //call destructor
  1c:   c9                      leave //restore esp and ebp
  1d:   c3                      ret //return
  1e:   90                      nop
  1f:   90                      nop