1

我想对 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

所以我可以理解前 3 行,我需要一些帮助才能理解其余部分

感谢您的关注!

4

2 回答 2

9

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

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

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

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

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

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

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

于 2013-05-14T12:19:19.330 回答
6

自动变量通常在堆栈上分配,但它们的实现当然可以使用其他内存。对于标准容器,它们将使用动态内存分配或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
于 2013-05-14T12:18:41.020 回答