看到一篇比较有意思的文章,在这里就翻译一下,当今天发表的内容了。
from http://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/
一个程序对于内存的使用,一般可以分为四类:
1. 代码区域,通过的编译的程序在代码中存储的位置
2. 全局变量区域,全局变量存储的位置
3. 堆,动态内存分配的变量存储的位置
4. 栈,函数参数和局部变量存储的位置
下面将主要针对3,4两个方面进行论述。
堆
堆是一个用于动态分配的很大的内存池。在C++中,当你使用new操作符分配内存时,这个内存是从堆中取得的。
1: int *pValue = new int; // pValue is assigned 4 bytes from the heap
2: int *pArray = new int[10]; // pArray is assigned 40 bytes from the heap
因为,要分配的内存的准确位置事先是不知道的,所以分配的内存是间接获取的,也就说明了为什么new操作符返回的是指针。这里你并不需要担心背后内存具体如何分配的机制。但是,有一点值得明了,连续的内存请求所指向的很可能不是连续的地址的内存片段。如:
1: int *pValue1 = new int;
2: int *pValue2 = new int;
3: // pValue1 and pValue2 may not have sequential addresses
上例中,两个new是连续的,但是分配得到的pValue1和pValue2的地址是不连续的。当动态分配的变量被删除后,内存有被返回到堆中以备后续中再次被动态分配。
堆有如下优缺点:
1. 分配的内存一直处于被分配的状态,知道去分配操作出现,才回到初始状态(没有及时去分配,如new后没有用delete,会导致内存的泄漏)。
2. 动态分配的内存必须通过指针联系
3. 由于堆是一个很大的内存池,大的数组,结构,类都应该在这里分配得到内存
栈
栈调用扮演了一个很有趣的角色。在我们讨论栈调用之前,让我们讨论一下什么叫做栈。
想象一下自助餐厅中的一堆叠起来的盘子。因为每个盘子都比较重,它们被叠起来的,你只能做以下三件事情。
1)看向最上面的盘子
2)将最上面的盘子拿掉
3)在上面放一个新的盘子
在计算机编程中,栈是一个存储其他变量的容器(很像一个数组)。但是,一个数组能够以你想要的方式任意更改获取其中的元素,栈则有更多的限制,栈中能够进行的操作如下:
1)查看栈顶的项
2)将栈顶的项从顶部移除
3)将一个新的项放置顶部
栈是后进先出(LIFO)的数据结构。
(略掉一些内容······)
我们主要来看当调用函数是栈发生了一些什么行为。
1)发生函数调用处的相关指令的地址被push进栈中
2)函数返回类型置于栈中,设置一个占位符
3)CPU跳至函数的代码中
4)现在栈顶的内容是一个特殊指针,栈帧。随后所有的加入到栈中的内容都是该函数的局部信息。
5)所有的函数参数都被放入栈中
6)函数中的指令得到执行
7)函数中的局部变量push栈中
当函数结束的时候,发生如下事件:
1)函数的返回值拷贝进入一个占位符中,
2)在栈帧之后的所有内容都被pop off,所有的局部变量都被销毁
3)返回值被pop off并赋值给函数中的变量,如果调用函数的返回值没有赋值给任何东西,该值就会丢失
4)下一个语句的地址从栈中pop off,CPU继续执行
栈溢出
栈的大小是有限的,只能有序的容纳一定量的信息。如果程序试图向栈中放入过多的信息,栈将会溢出。栈溢出发生在栈的内存都被分配完了,那种情况下,更多的分配,将会发生在内存的其它片段中。
栈溢出通常发生在分配了太多的变量到栈上,或太多的嵌套函数调用,导致结果就是程序的奔溃。
如:
1: int main()
2: {
3: int nStack[100000000];
4: return 0;
5: }
栈的优缺点:
* 栈中的内容可以一直保留到pop off发生
* 编译时,栈中分配的内存是已知的,因此,可以直接通过变量访问内存。
* 因为栈相对而言是比较小的,所以小心会吃掉很多栈空间的事情。包括大型数组,结构体,类过多的内嵌,过多的递归等。