对C ++动态内存分配的确切含义有点困惑
我听说过有关动态确切含义的矛盾事件,以及关于自动内存分配的问题。 我听说这个堆栈被称为自动内存分配和动态内存分配。 我可以看到,因为堆栈内存块大小是在程序执行前确定的,所以它的最大大小在运行时不能增长。 但是,在程序执行过程中,随着函数数据被推入并弹出堆栈,堆栈不断增长和收缩。
那么从这个意义上讲,这不是动态的内存分配?
如果是这样的话,只是将堆称为动态的,这不是令人困惑吗?
有人可以帮我澄清一下吗?
编辑:看来我混淆了某些在撰写本文时我并不知晓的概念。 低级别堆栈和堆内存管理与C ++中相同事物的高级概念之间存在差异。 有关此方面的澄清,请参阅下面我接受的答案。
我会尽我所能地解决困惑。 首先,学习从c ++级存储器概念中分离低级存储器模型概念(堆栈,堆)。 在C ++的世界里, stack
和heap
并不意味着任何类似堆栈或堆的低层模型。
低级内存模型
首先,我们来谈谈低级别内存模型。 传统上,内存被分割为'堆栈'和'堆'内存,我将在后面介绍。
堆
堆栈由所谓的“堆栈指针”CPU寄存器进行管理 - 它总是指示堆栈的顶部,并从高级内存地址连续到低级内存地址。 由于堆栈顶部总是由寄存器指向,因此不需要任何与堆栈相关的实际内存管理 - 当您需要更多内存时,只需减少存储在指针中的值 - 这就是您的内存,它现在是被认为是分配给你的。 当你不再需要内存时,你可以增加值 - 而内存现在是“免费”的。 显然,这种方法的问题在于它不可持续 - 你不能在块内释放(或分配)内存。 所以如果你为3个对象分配内存,A,B,C,而你不再需要对象B,那么你就不需要说B被占用的内存是可以自由使用的 - 单个堆栈指针根本没有能力这样做。
这将堆栈内存的使用限制为“近距离”,短期对象的情况 - 当您知道不需要有选择地释放与在此范围内分配的对象相关联的任何内存时,可以将所有内存他们很快就足够了。 这使得堆栈内存成为函数中定义的变量的理想存储 - 当函数退出时,所有这些变量都被释放。 更好的是,编译器可以自动为你做这些 - 你不必明确地告诉编译器何时为每个变量释放内存 - 一旦代码执行离开它的范围,它将被自动释放。
值得注意的是堆栈分配和释放是非常快的 - 它们只需要一个寄存器算术操作。
但是,正如我之前所说的,堆栈有限制。 堆内存是为了克服这些问题 - 接下来将会介绍。
堆
与堆栈(仅由简单寄存器管理)不同,堆内存由复杂的结构和逻辑支持。 您可以从堆中请求内存,并且可以将内存返回到堆中,并且可以为每个对象独立执行。 因此,回到我的原始示例,当您为对象A,B和C(所有大小相同)请求内存并且不再需要对象B时,可以为B返回内存并仍保留A和C.如果需要创建另一个与以前大小相同的对象D,并为它寻找内存,堆可以给你从B返回的内存。尽管不能保证(堆算法非常复杂),但这是一个很好的简化。
与堆栈内存不同,管理堆内存具有成本,实际上它相当高(特别是在多线程环境中)。 这就是为什么堆内存不应该被使用,如果有人可以帮助它,但这是它自己的一个巨大的话题,我现在不会详谈。
堆内存的一个非常重要的特性是它必须由用户明确管理。 您需要时请求记忆,当您不再需要记忆时请将其退回,并且绝对不要使用您提供的记忆。 不遵守这些规则会使你的程序泄漏内存 - 也就是说,消耗内存而不回馈它,这会导致程序最终耗尽内存 - 以防你不回忆内存; 或者导致程序行为不正确(如果您在请求之前或之后使用内存),因为您将访问不属于您的内存。
C / C ++内存模型
无论好坏,C / C ++都将程序员从低级内存概念中解放出来。 相反,该语言指定每个变量都存在于某种类型的存储中,并且它的存活时间由存储类型定义。 有三种类型的存储,如下所述。
自动存储
这个存储由编译器自动管理(因此是名称),并不要求程序员对此做任何事情。 自动变量的一个例子是在函数体中定义的一个例子:
void foo() {
int a;
}
a
在这里是自动的。 您不必担心为它分配内存或在不再需要时清除它,并且编译器保证当您输入函数foo()时它将在那里,并且当您退出foo()时它将不再存在, 。 虽然它可能被分配在堆栈上,但是绝对不能保证 - 它可能会被放入注册表中。 寄存器比任何内存快得多,因此编译器会尽可能地利用它们。
静态存储
变量放入静态存储器直到程序退出。 同样,开发人员不需要担心他们的生命周期或清理内存 - 在程序退出后,内存将被清理,而不是之前。 静态持续变量的一个例子是一个变量,它定义在任何函数(全局变量),函数的静态局部变量以及类的静态成员之外。 在下面的代码中,var1,var2和var3都是静态存储中的变量:
代码(带有一些内嵌评论):
int var1;
void foo() {
static int var2;
}
class A {
static int var3;
}
动态存储
动态存储变量由开发人员控制。 当你需要它们的时候,你需要这些内存(通常在C中使用malloc
或者在C ++中使用new
),并且当你不再需要它的时候,你必须把它回收(在C中是free
的,在C ++中是delete
)。 作为一名开发人员,您应该充分注意如何分配,使用和删除这些内容,并确保序列永不中断。 不遵守序列是造成新闻的所有重大程序错误的唯一主要原因:)。 幸运的是,C ++为你提供了特殊的功能和类来简化这个任务,但是如果你用C语言开发,那么你就是自己的。 在下面的例子中,动态分配var4点的内存。
码:
void foo() {
int* var4;
// Here is the major source of confusion. var4 itself is **automatic**
// you do not need to allocate or free var4 memory, so you can use it
// like this:
var4 = NULL; // Not an error!!!
// However, you can't use the memory var4 points to yet!
// Following line would cause incorrect behavior of the program:
// *var4 = 42; // NEVER EVER!!!
// Instead, you need to allocate the memory first (let's assume, we are in C++
var4 = new int();
// Now the memory was allocated, we can use it
*var4 = 42; // Correct!
// we no longer need this memory, so let's free it:
delete var4;
// This did not change var4 itself (unless there is a special case)
// so technically, it still points to the memory which was former
// belonging to you. But the memory is no longer yours!!!
// you can't read or write it!
// Following code is bad-bad-bad:
// int x = *var4; // NEVER EVER!
}
正如你所看到的,使用动态存储器带有大多数警告和警告标志。 这就是为什么在C ++中有特殊的功能可以使这更简单,并且没有人需要编写上面写的代码。 然而,我的职位已经的方式来长,在C ++所以正确的内存管理将留给另一个场合:)
根据维基百科:
C动态内存分配是指执行手动内存管理。
在这个意义上堆栈不是动态的,因为堆栈变量的大小必须在编译时已知:
C编程语言静态 , 自动或动态地管理内存。 静态持续变量通常与程序的可执行代码一起分配到主存储器中,并在程序的整个生命周期中持续存在; 自动持续时间变量分配在堆栈上,并随着函数的调用而返回。 对于静态持续时间和自动持续时间变量,分配的大小必须是编译时常量。 [...]分配内存的生命周期也会引起关注。 [...]通过使用动态内存分配可以避免这些限制。
实际上,人们可以通过几种方式动态分配内存:
alloca()
函数; dynarray
容器。 然而,堆栈内存的这些用途并不典型,也许更重要的是,这样做仍然不能提供关于对象生命周期的额外灵活性。
你的第二个困惑是关于堆栈的增长。 是的,堆栈的最大尺寸是静态确定的。 但是,最大尺寸比普通程序要大得多(在Linux上默认为8 MB),甚至可以在运行时通过使用操作系统API(Linux上的setrlimit
)更改该数字。 在程序执行期间,堆栈的实际大小会动态增长和缩小,直到达到此限制。
上一篇: A bit confused on exact meaning of dynamic memory allocation for C++