动态内存和"普通"内存之间的差异

Xon*_*ara 11 c++ memory memory-management

分配给new运算符的内存与通过简单变量声明分配的内存之间有哪些技术差异,例如int var?c ++是否有任何形式的自动内存管理?

特别是,我有几个问题.首先,由于使用动态内存,您必须声明一个指针来存储您使用的实际内存的地址,动态内存不会使用更多内存吗?除非你声明一个数组,否则我不明白为什么指针是必要的.

其次,如果我要做一个像这样的简单函数:

int myfunc() { int x = 2; int y = 3; return x+y; }
Run Code Online (Sandbox Code Playgroud)

...并且调用它,一旦它的存在范围结束,函数分配的内存是否会被释放?动态记忆怎么样?

Wes*_*ley 20

注意:这个答案是方式太长.我会在某个时候削减它.同时,如果您能想到有用的编辑,请发表评论.


要回答您的问题,我们首先需要定义两个内存区域,称为堆栈.

堆栈

想象一下堆栈是一堆盒子.每个框表示一个函数的执行.一开始,当main被叫时,地板上有一个盒子.您定义的任何局部变量都在该框中.

一个简单的例子

int main(int argc, char * argv[])
{
    int a = 3;
    int b = 4;
    return a + b;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你在地板上有一个框,包含变量argc(整数),argv(指向char数组的指针),a(整数)和b(整数).

不止一盒

int main(int argc, char * argv[])
{
    int a = 3;
    int b = 4;
    return do_stuff(a, b);
}

int do_stuff(int a, int b)
{
    int c = a + b;
    c++;
    return c;
}
Run Code Online (Sandbox Code Playgroud)

现在,你有在地板上的箱子(为main)有argc,argv,a,和b.在那个盒子上面,你有另一箱(为do_stuff)有a,bc.

此示例说明了两个有趣的效果.

  1. 正如您可能知道的那样,a并且b是按值传递的.这就是为什么在框中有这些变量的副本do_stuff.

  2. 请注意,您不必freedelete或这些变量的任何东西.函数返回时,该函数的框将被销毁.

盒子溢出

    int main(int argc, char * argv[])
    {
        int a = 3;
        int b = 4;
        return do_stuff(a, b);
    }

    int do_stuff(int a, int b)
    {
        return do_stuff(a, b);
    }
Run Code Online (Sandbox Code Playgroud)

在这里,你在地板上有一个盒子(main和以前一样).然后,你有一个盒子(for do_stuff)用ab.然后,你有另一个框(用于do_stuff调用自己),再次使用ab.然后另一个.很快,你有一个堆栈溢出.

堆栈摘要

将堆栈视为一堆盒子.每个框表示一个执行的函数,该框包含该函数中定义的局部变量.函数返回时,该框被销毁.

技术更多的东西

  • 每个"盒子"官方称为堆栈帧.
  • 有没有注意到你的变量如何具有"随机"默认值?当旧的堆栈帧被"破坏"时,它就会停止相关.它不会被归零或类似的东西.下一次堆栈帧使用该部分内存时,您会在本地变量中看到旧堆栈帧的位.

这是动态内存分配发挥作用的地方.

想象一下堆是无尽的绿色记忆草甸.当你调用mallocor时new,在堆中分配了一块内存.您将获得一个指针来访问此内存块.

int main(int argc, char * argv[])
{
    int * a = new int;
    return *a;
}
Run Code Online (Sandbox Code Playgroud)

这里,在堆上分配一个新的整数值的内存.你得到一个指向a该内存的指针.

  • a是一个局部变量,所以它在main's"框中"

动态内存分配的基本原理

当然,使用动态分配的内存似乎浪费了几个字节来指示.但是,如果没有动态内存分配,有些事情你不能(轻松)做到.

返回一个数组

int main(int argc, char * argv[])
{
    int * intarray = create_array();
    return intarray[0];
}

int * create_array()
{
    int intarray[5];
    intarray[0] = 0;
    return intarray;
}
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?你"返回一个数组" create_array.实际上,您返回一个指针,该指针只指向create_array包含该数组的"框" 部分.create_array返回时会发生什么?它的盒子被破坏了,你可以期待你的阵列随时变得腐败.

而是使用动态分配的内存.

int main(int argc, char * argv[])
{
    int * intarray = create_array();
    int return_value = intarray[0];
    delete[] intarray;
    return return_value;
}

int * create_array()
{
    int * intarray = new int[5];
    intarray[0] = 0;
    return intarray;
}
Run Code Online (Sandbox Code Playgroud)

因为函数返回不会修改堆,所以你的宝贵intarray逃脱毫发无损.delete[]在你完成之后记住它.


Rau*_*ait 5

动态内存位于堆上,而不是堆栈上。动态内存的生命周期是从分配时间到释放时间。对于局部变量,它们的生命周期仅限于定义它们的函数/块。

关于您关于函数中内存使用情况的问题,在您的示例中,局部变量的内存将在函数结束时释放。但是,如果内存是使用 动态分配的new,则不会自动释放内存,并且您将负责显式使用delete来释放内存。

关于自动内存管理,C++标准库为此提供了auto_ptr。