堆 heap 和 栈 Stack在 C++中的区别
文中所有的例子和内容均来自于如下视频讲解:
https://www.youtube.com/watch?v=wJ1L2nSIV1s&list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb&index=54
堆(heap)和栈 (stack) 是 RAM(Random Access Memory) 中的两块内存区域。栈 (Stack)实际上是预先规定好大小的一块内存区域,通常大小为两百万个字节 (2 megabytes)。堆 (heap)实际上也有预先定义好的大小,但是这个大小是可变的。可以随着我们的应用程序的大小而改变。不过事实上,这两块内存区域在内存中的物理地址都是一样的,都存在于我们的RAM中。
既然实际上的存储区域是一样的,那不同的只是在于堆和栈如何响应我们代码的内存请求。
下面为如何在 stack 和 heap 上申请空间来存储变量的示例,其中用关键字 new 定义的变量,则将存储于 heap 中。
int main() {
//请求栈中内存
int value = 5;
int array[5];
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;
//用关键字 new 请求堆中内存
int* hvalue = new int;
*hvalue = 5;
harray[0] = 1;
harray[1] = 2;
harray[2] = 3;
harray[3] = 4;
harray[4] = 5;
delete hvalue;
delete[] harray;
std::cin.get();
}
如果我们直接看内存空间的状态,实际上在stack中开辟空间就等于将指向栈顶的指针往后移动要求的大小,如果是int, 则向后移动 4 bytes. 在 stack 里变量实际上是存在每个变量的上面,就像一个数据结构里的 stack. 当然,在大多数的stack实现中,stack 实际上是向后增长的。比如,在上述示例中,int value 是存在比较高的内存地址中, 如 0x00B5FE54, 而 array 则存在相对较低的内存地址中,如 0x00B5FE38。 当然,每次我们只是将变量放在上一个变量的上面,所以 stack 是非常快的。 比如,我们要在 stack 中申请一个 4 bytes 的内存空间,则只是将 stack 的指针移动 4bytes, 然后返回当前的指针指向的地址,这个地址则是该变量的开始地址。
而对于下面的在heap中申请存储空间而言,hvalue 和 harray 的地址空间是完全不同的,它们不是连续的。当然从实际内存中来看,在heap中申请一个新的内存空间,首先是在 stack 中申请了一个 4 bytes 的内存空间来存放指向实际heap中存储区域的指针,然后再在 heap 中开辟实际存储空间。所以我们在上述申请在 heap 中的空间时,返回的类型都是指针类型。而关键字 new 实际上是调用 malloc 方法,该方法会调用底层的操作系统的特定方法来分配内存空间。当你创建一个应用程序的时候,一定大小的 RAM 的空间会分配给你。而你的程序会维护一个叫做 free list 的列表,用于存储那一块内存空间是可用的,位于哪里。当你动态请求内存空间的时候,malloc 会去 free list 中查找,它会给你一个指向该内存的地址,同时记录分配的地址和大小。
总结起来,在 stack 中分配内存空间仅仅相当于一条 CPU 指令,机器指令仅仅是将 5 移到寄存器。而如果在 heap 中申请内存空间的话,操作就非常多了,调用 malloc,查找 free list, 等等。 所以 heap 存储实际上要慢于 stack 存储。
另外,在 stack 里申请的内存空间在程序运行到 ’}‘ 时,该内存地址则会自动释放。而在 heap 中申请的内存空间则并不会自动释放,需要我们手动调用 delete 删除,否则会造成 memory leak.