【问题标题】:Organization of a c++ program in memory - stack and heap [duplicate]在内存中组织 C++ 程序 - 堆栈和堆 [重复]
【发布时间】:2014-05-22 08:41:16
【问题描述】:

我正在学习 c++,想知道这样的程序是如何在主内存中组织的。我知道有一个堆栈(带有堆栈帧)和一个堆。而且我知道动态分配某些东西会在堆上分配它。这是由mallocnew 等运算符完成的。但是我在这个小的 c++ 程序中看不到它们。

该程序由一个主类和一个名为MyClass 的类组成。这个类有:

  • 一个构造函数
  • 一个成员变量 (int)
  • 一个成员函数

主类为 Myclass 定义了一个对象,同时也定义了一个指向该对象的指针。

所以 - 所有这些在内存中是如何组织的?

#include <iostream>
using namespace std;

class MyClass {
    int i;
public:
    MyClass(int n) {
        i = n;
    }
    int get_nmbr() {
        return this->i;
    }
};


int main() {
    MyClass myClass(100), *p;
    cout << myClass.get_nmbr() << endl;
    p = &myClass;
    cout << p;
    return 0;
}

【问题讨论】:

  • 这在很大程度上取决于您的实现和架构。从 C++ 本身的 pov 来看,实现的方式并不重要。
  • 您看不到mallocnew,因为程序中的所有内容要么放在寄存器中,要么放在堆栈中。顾名思义,stack 就像一堆书,遇到的每一件事都堆在上面,必须先删除。您可以预期类似:100 加载到寄存器中,myClass 被认为处于“最低”堆栈位置,然后pmyClass 的地址放入寄存器,MyClass() 调用构造函数(返回地址推送到堆栈) ,从寄存器中取出this,将100写入this-&gt;i(堆栈),弹出返回地址,推送寄存器,调用operator&lt;&lt;(ostream, int)....

标签: c++ memory heap-memory stack-memory


【解决方案1】:

让我们逐行浏览。

int main() {

一个新函数从这一行开始,然后是一个新的scope

    MyClass myClass(100), *p;

这里发生了两件事。一,变量myClass在函数的作用域内声明,这使它成为一个局部变量,因此它被分配在堆栈上。编译器将发出在堆栈上保留足够空间的机器指令(通常通过碰撞sp 堆栈指针寄存器),然后执行对类构造函数的调用。传递给构造函数的this 指针是堆栈分配的基础。

第二个变量p 只是一个本地指针,编译器(取决于优化)可以将此值存储在本地堆栈或寄存器中。

   cout << myClass.get_nmbr() << endl;

调用本地myClass实例的get_nmbr()方法。同样,this 指针指向本地堆栈帧分配。此函数查找实例变量i 的值并将其返回给调用者。请注意,因为对象是在栈帧上分配的,所以i 也存在于栈帧上。

   p = &myClass;

myClass 实例的地址存储在变量p 中。这是一个堆栈地址。

   cout << p;
   return 0;
}

打印出局部变量p并返回。

您的所有代码只关心堆栈分配。其结果是,当函数的作用域在执行时离开/关闭时(例如函数返回),对象将自动“销毁”并释放其内存。如果您从该函数返回了像 p 这样的指针,那么您正在查看 dangling pointer,即指向已释放和销毁的对象的指针。 (根据语言标准,通过这种悬空指针访问内存的行为是“未定义的”。)

如果你想在堆上分配一个对象,从而将它的生命周期扩展到它声明的范围之外,那么你在 C++ 中使用new operator。在后台,new 调用malloc,然后调用适当的constructor

您可以将上面的示例扩展为类似以下内容:

{
    MyClass stackObj(100);  // Allocate an instance of MyClass on the function's stack frame
    MyClass *heapObj = new MyClass(100); // Allocate an instance of MyClass from the process heap.

    printf("stack = %p heap = %p\n", stackObj, heapObj);

    // Scope closes, thus call the stackObj destructor, but no need to free stackObj memory as this is done automatically when the containing function returns.
    delete heapObj; // Call heapObj destructor and free the heap allocation.
}

注意:您可能想看看placement new,在这种情况下可能还想看看auto pointersshared pointers

【讨论】:

    【解决方案2】:

    首先要做的事情。在 C++ 中你不应该使用malloc

    在这个程序中,所有使用的内存都在堆栈上。让我们一次看一个:

    MyClass myClass(100);
    

    myClass 是堆栈上的自动变量,大小等于sizeof(MyClass);。这包括成员i

    MyClass *p;
    

    p 是堆栈上的一个自动变量,它指向MyClass 的一个实例。由于它没有被初始化,它可以指向任何地方并且不应该被使用。它的大小等于sizeof(MyClass*);,它可能(但不一定)在myClass 之后立即放在堆栈上。

    cout << myClass.get_nmbr() << endl;
    

    MyClass::get_nmbr() 返回myClass 实例的成员i 的副本。该副本可能已被优化掉,因此不会占用任何额外的内存。如果是这样,它将在堆栈上。 ostream&amp; operator&lt;&lt; (int val); 的行为是特定于实现的。

    p = &myClass;
    

    这里p 指向一个有效的MyClass 实例,但由于该实例已经存在,内存布局没有任何变化。

    cout << p;
    

    输出myClass的地址。 operator&lt;&lt; 的行为再次是特定于实现的。

    This program(有点)可视化自动变量的布局。

    【讨论】:

      【解决方案3】:

      您在堆栈上创建了 myClass 对象和指针 p(声明为 MyClass *)。所以。在 myClass 对象中,数据成员也作为 myClass 对象的一部分在堆栈上创建。指针 p 存储在堆栈中,但您没有为 p 分配,而是将 p 分配给已存储在堆栈中的 myClass 对象的地址。所以,这里没有堆分配,至少从您发布的程序段中。

      【讨论】:

        【解决方案4】:

        myClass 在堆栈上分配。该程序可能不使用堆 exept 在幕后分配标准输出缓冲区。当myClass 超出范围时(例如当main 返回或抛出异常时),myClass 被破坏并释放堆栈,使p 无效。

        在现代 C++ 中,使用智能指针(例如 shared_ptr)而不是原始指针被认为更安全。
        要在堆上分配myClass,您可以这样写:

        #include <memory>
        
        int main() {
          std::shared_ptr<MyClass> p (new MyClass (100));  // Two heap allocations: for reference counter and for MyClass.
          auto p2 = std::make_shared<MyClass> (101);  // One heap allocation: reference counter and MyClass stored together.
          return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-15
          • 2012-09-23
          • 2011-04-13
          • 2011-09-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多