在内存中组织c ++程序 - 堆栈和堆

时间:2014-04-09 09:00:10

标签: c++ memory stack heap

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

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

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

main-class定义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;
}

4 个答案:

答案 0 :(得分:6)

让我们逐行了解。

int main() {

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

    MyClass myClass(100), *p;

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

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

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

调用本地get_nmbr()实例的myClass方法。同样,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

答案 1 :(得分: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()返回i实例的成员myClass的副本。这个副本可能已经过优化,因此不会占用任何额外的内存。如果确实如此,它将在堆栈中。 ostream& operator<< (int val);的行为是特定于实现的。

p = &myClass;

此处p指向有效的MyClass实例,但由于实例已存在,因此内存布局没有任何变化。

cout << p;

输出myClass的地址。 operator<<的行为再一次是特定于实现的。

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

答案 2 :(得分:1)

在堆栈上分配了

myClass。该程序不使用堆exept可能用于在后台分配stdout缓冲区。当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;
}

答案 3 :(得分:1)

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