我对这三个概念有些困惑:堆栈,堆和动态内存分配。我将用C ++提供示例。
我是说一个程序,对于它的所有变量,数组,也许还有堆栈上的对象,当程序刚启动时,所需的所有存储空间已经存在,所以所有的东西都是预先确定的吗?但是在程序运行时,对我来说,它仍然听起来像“动态的”,因为堆栈仍在变化,从某种意义上说,值仍然在运行时被压入堆栈,弹出堆栈。
关于堆,出于“动态”的意义,我从这个站点上的一些答案中得到了这个想法,它是针对运行时确定的:
cin >> box_size;
int *box = new int[box_size];
但是,接下来呢†:
Box surprise_box = new Box();
我已经知道编译时需要多少空间了吗?但是它仍然在堆上。这样看来,“动态”内存分配正好成为程序员分配/分配内存的负担之一。
†:应该改为Box *ptr_surprise_box = new Box();
。 (感谢您的评论)
我理解为什么我的问题被认为过于笼统,但是我不知道如何将它们分成几部分。更糟糕的是,现在我正在考虑这是否与地址空间以及在编译时确定的内存地址有关。
答案 0 :(得分:4)
我对这三个概念有些困惑:堆栈,堆和动态内存分配。
您当然是在混合不同领域的概念-前2个与OS相关,最后与语言相关。
对于C ++,没有诸如堆栈或堆之类的东西。 C ++中的对象有4种不同的存储期限:自动,静态,线程和动态。是的,具有自动存储时间的对象通常存储在堆栈中,而具有动态存储时间的对象通常存储在堆中,但这就是实现的细节-从语言的角度来看,没有这种东西。有关存储期限的详细信息,请参见here
答案 1 :(得分:3)
我发现查看堆与堆栈的最好方法不是“动态”或“在编译时知道大小”(尽管这可能是因素)。相反,最好从生命周期管理的角度来看它们。
当超出范围时,分配在堆栈上的东西将被销毁。在堆上分配的内容将一直存在,直到使用delete
,free
或delete[]
(取决于它们的分配方式)显式释放它们为止。
要回答您的问题:
我正确地说,对于一个程序,对于它的所有变量, 程序刚启动时的数组,甚至可能是堆栈上的对象, 所有需要的存储空间已经存在,因此一切 预定的。
嗯,在每个功能的基础上,有点...,但是这种观点并不能真正说明函数可以执行诸如调用自身(递归)之类的操作并到达任意堆栈的事实。大小用法。
因此,“动态”内存分配似乎正好成为程序员分配/分配内存的负担之一。
是的,根本的区别实际上在于生命周期管理。
答案 2 :(得分:2)
stack
或heap
,在其他段中也可能有全局变量,这是一个很长的故事。)堆栈空间是由编译器分配的,当您输入函数时,编译器生成的代码会自动为堆栈变量分配足够的空间。
堆空间由您分配,当您需要一些内存时,可以调用一个函数为您分配堆空间。
输入您的代码
cin >> box_size;
int *box = new int[box_size];
这里box
是堆栈上的变量。 box
变量的值是指向堆上内存的指针。
但是Box surprise_box = new Box();
无效的语法。
示例:
Box box1;
:变量box1
在堆栈中。
Box *box2 = new Box();
:box2
的变量在堆栈上,它是一个指针,指向new Box()
的内存
已更新:
没有关于“堆”和“堆”概念的明确的“动态”定义。例如,在C99中,如果您执行scanf("%d", &size); int a[size]
;仍然可以,并且a
可以在堆栈上,而不是堆上。这完全取决于编译器的行为。您分配的内存通常在堆上(您称为内存分配函数,包括new()
),编译器代码分配的内存通常在堆栈上。
ps:我认为@Slava的答案是相当不错的。这些概念在不同的领域。 stack
和heap
主要与操作系统相关,dynamic
和static
主要与语言相关。我刚刚谈到了现代C ++语言的大多数实现,它们碰巧将new()
内存放入堆中,等等。
答案 3 :(得分:1)
用户slava提到了这些:
automatic
,static
,thread
和dynamic
除了thread
之外,我将重点关注其他所有内容。他还这样说:
对于C ++,没有诸如堆栈或堆之类的东西
用户Even Teran说过:
我发现查看堆与堆栈的最好方法不是“动态”或“在编译时知道大小”(尽管这可能是因素)。相反,最好从生命周期管理的角度来看它们。
当超出范围时,分配在堆栈上的东西将被销毁。在堆上分配的内容将一直存在,直到使用delete,free或delete [](取决于它们的分配方式)显式释放它们为止。
我将通过演示将先前的两个答案合并为一个答案:
Automatic Storage
int main () {
int x = 5;
{ // new scope
int x = 7;
std::cout << x << '\n'; // prints 7
} // end scope
std::cout << x << '\n'; // prints 5
return 0;
}
这被视为Automatic Storage
,因为变量声明和内存一旦超出范围就会被销毁。从某种意义上来说,您可以将其视为stack
变量,因为在C ++中,每个function
都有一个stack frame
。以上两个变量x
都位于main.cpp
的堆栈框架中,但具有automatic storage
。但是,对std::cout
的第一次调用将打印7
,因为它在声明为x
的2 nd 变量的同一范围内。一旦到达closing
大括号}
,此作用域就会被破坏,第二个 nd x
变量也会被破坏。因此,当我们到达下一个对std::cout
的调用时,它将打印5
,因为它在声明的x
的1 st 的同一范围内。
Dynamic Storage
int main() {
int x = 5; // Automatic storage
int* ptr = nullptr; // raw pointer
ptr = new int(x); // Dynamic Storage
std::cout << "x = " << x << '\n'; // prints 5
std::cout << "*ptr = " << *ptr << '\n'; // prints 5
{ // new scope
*ptr = 12;
} // end scope
std::cout << "x = " << x << '\n'; // x unchanged prints 5
std::cout << "*ptr = " << *ptr << '\n'; // prints 12
delete ptr; // clean up memory
return 0;
}
Dynamic Storage
的寿命比声明的作用域更长。您可以将动态内存从一个函数传递到另一个函数,只要该函数所引用的原始对象仍然有效,否则就会发生内存泄漏,无效或悬空的指针可能导致不良行为,不确定行为,程序崩溃,操作系统被擦除以及核弹爆炸! (最后一点有点戏剧性;但是不要笑,因为糟糕的内存管理将对您的代码库造成严重破坏,所有WHO都将使用它!)。这使您可以存储数据并将其从一个功能修改为另一个功能,而不必每次在其他计算中引用它时都在其副本上创建副本。
Static Storage
static int i = 0; // static storage: life time of application or file scope; similar to a global...
void add1() {
i++;
}
int main() {
std::cout << i << '\n';
add1();
std::cout << i << '\n';
for ( int n = 0; n < 10; n++ ) {
add1();
std::cout << "n=" << n << ": " << i << '\n';
}
return 0;
}
输出
0
1
n=0: 2
n=1: 3
n=2: 4
n=3: 5
n=4: 6
n=5: 7
n=6: 8
n=7: 9
n=8: 10
n=9: 11
Static Storage
有所不同。不需要像动态内存一样对其进行清理,因为它具有与Automatic Storage
类似的属性,因为它将被自动销毁。但是,它们通常在global namespace
或global file space
中可见,它们很有用,但如果管理不当或使用不当,则全局变量又可能很危险。如果在main.cpp
中定义,则它们通常具有程序应用程序的生存期,如果在其他cpp文件中定义,则它们具有文件作用域的生存期。与Static Storage
的唯一不同之处在于,它也只初始化一次,而且通常只有一个实例!
是的,存在不同类型的存储类,并且许多存储类仍将它们称为stack
和heap
,这主要是由于C++
是基于C
构建的;但是多年来的含义和用法已经发生了巨大变化。在C++
中,与位于内存中的where
相比,它与变量的生存期和可见性有关。