使用new
运算符分配的内存与通过简单变量声明分配的内存(例如int var
)之间有哪些技术差异? c ++是否有任何形式的自动内存管理?
特别是,我有几个问题。首先,由于使用动态内存,您必须声明一个指针来存储您使用的实际内存的地址,动态内存是否使用 more 内存?除非你声明一个数组,否则我不明白为什么指针是必要的。
其次,如果我要做一个像这样的简单函数:
int myfunc() { int x = 2; int y = 3; return x+y; }
...并且调用它,一旦它的存在范围结束,函数分配的内存是否会被释放?动态记忆怎么样?
答案 0 :(得分:20)
注意:这个答案方式太长了。我会在某个时候削减它。同时,如果您能想到有用的编辑,请发表评论。
要回答您的问题,我们首先需要定义两个记忆区域,分别称为堆栈和堆。
想象一下堆栈是一堆盒子。每个框表示一个函数的执行。一开始,当调用main
时,地板上有一个盒子。您定义的任何局部变量都在该框中。
int main(int argc, char * argv[])
{
int a = 3;
int b = 4;
return a + b;
}
在这种情况下,你在地板上有一个框,变量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;
}
现在,您在main
,argc
,argv
和a
的地板上(b
)有一个方框。在该框的顶部,您有另一个包含do_stuff
a
,b
和c
的框(a
)。
这个例子说明了两个有趣的效果。
您可能知道,b
和do_stuff
是按值传递的。这就是free
框中存在副本的原因。
请注意,这些变量不需要delete
或 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);
}
或其他任何内容。当您的函数返回时,该函数的框将被销毁。
main
在这里,你在地板上有一个方框(对于do_stuff
,和以前一样)。然后,您有一个包含a
和b
的{{1}}}框。然后,您再次使用do_stuff
和a
来设置另一个框(b
调用自身)。然后另一个。很快,你有一个堆栈溢出。
将堆栈视为一堆盒子。每个框表示一个执行的函数,该框包含该函数中定义的局部变量。当函数返回时,该框被销毁。
这是动态内存分配发挥作用的地方。
想象一下堆是无尽的绿色记忆草甸。当您调用malloc
或new
时,会在堆中分配一块内存。您将获得一个指针来访问此内存块。
int main(int argc, char * argv[])
{
int * a = new int;
return *a;
}
这里,在堆上分配一个新整数的内存。你得到一个名为a
的指针指向那个记忆。
a
是一个局部变量,因此它位于main
的“框”中当然,使用动态分配的内存似乎浪费了几个字节来指示。但是,如果没有动态内存分配,有些事情你不能(轻松)做到。
int main(int argc, char * argv[])
{
int * intarray = create_array();
return intarray[0];
}
int * create_array()
{
int intarray[5];
intarray[0] = 0;
return intarray;
}
这里发生了什么?你在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;
}
因为函数返回不会修改堆,所以您的宝贵intarray
没有受到损害。完成后请记住delete[]
。
答案 1 :(得分:4)
动态内存存在于堆上,而不是堆栈。动态内存的生命周期是从分配时到释放时。对于局部变量,它们的生命周期仅限于它们所定义的函数/块。
关于函数中内存使用情况的问题,在您的示例中,函数末尾将释放本地变量的内存。但是,如果内存是使用new
动态分配的,则不会自动处理,您将负责明确使用delete
释放内存。
关于自动内存管理,C ++标准库为此提供了auto_ptr。
答案 2 :(得分:4)
“new”分配的内存最终会堆在堆上。
函数中分配的内存位于函数放置在堆栈中的函数内。
在此处阅读堆栈与堆分配:http://www-ee.eng.hawaii.edu/~tep/EE160/Book/chap14/subsection2.1.1.8.html
答案 3 :(得分:2)
使用new运算符分配的内存是从名为“heap”的内存部分获取的,而变量的静态分配是使用与procedure / function-calls共享的内存部分(“stack”)。
您只需要担心自己使用new创建的动态内存分配,编译时已知的变量(在源中定义)会在其作用域结束时自动释放(函数/过程结束,块) ,...)。
答案 4 :(得分:1)
“动态”和“普通”记忆之间的巨大差异在问题本身中得到了很好的反映。
C ++完全不支持动态内存。
当您使用动态内存时,您自己应对此负全部责任。你必须分配它。当你忘记这样做并尝试访问它时扔了你的指针,你会有很多负面的惊喜。此外,你必须释放记忆 - 当你以任何方式忘记它时,你会有更多的惊喜。这些错误属于C / C ++程序中最难发现的错误。
你需要一个额外的指针,因为不知何故你需要访问你的新内存。一些内存(如果是动态的或非动态的)首先是编程语言无法处理的内容。您需要访问它。这是由变量完成的。但是像C ++这样的语言中的变量存储在“普通”内存中。所以你需要有“指针” - 指针是间接的一种形式,它说“不,我不是你要寻找的价值,但我指向它”。指针是C ++访问动态内存的唯一可能性。
相比之下,“普通”内存可以直接访问,分配和释放由语言本身自动完成。
动态内存和指针是C ++问题的最大来源 - 但它也是一个非常强大的概念 - 当你做得对时,你可以用普通的内存做更多的事情。
这也是原因,很多库都有处理动态内存的函数或整个模块。 auto_ptr-example也在并行答案中提到,它试图解决问题,动态内存应该在方法结束时可靠地释放。
通常,只有在您真正需要的情况下才会使用动态内存。你不会使用它来拥有一个整数变量,而是拥有数组或在内存中构建更大的数据结构。