栈和堆原理介绍
栈: 可以把栈看成是一叠卡片,最上面的卡片表示程序的当前作用域,这往往就是当前正在执行的函数。当前函数中声明的所有变量都置于栈顶帧中,即占用栈顶帧的内存,这就相当于一叠卡片中最上面的一张卡片。如果当前函数调用了另一个函数,举例来说,一开始一叠卡片位于最底的卡片是main()函数,main()函数调用了foo()函数,则相当于在这一叠卡片上加了另一张卡片,这样foo()函数就有了自己的栈帧(就是指一块内存空间)以供使用。从main()传递到foo()的所有参数都会从main()栈帧复制到foo()栈帧中。 然后foo()函数又调用了bar()函数,则在这一叠卡片上又加了一张卡片,这样bar()就有了自己的栈帧(stack frame)以供使用,从foo()传递到bar()的参数就会从foo()栈帧复制到bar()栈帧中。 图片如下: foo()函数声明了一个整数值。
栈帧很有意义,因为栈帧可以为每个函数提供一个独立的内存工作区。如果一个变量是在foo()栈帧中声明的,那么调用bar()函数不会对它带来改变,除非你专门要求修改这个变量。另外,foo()函数运行结束时,栈帧既消失,该函数中声明的所有变量就不会再占用内存了。。
堆:堆是一段完全独立于当前函数或栈帧的内存区。如果一个函数中声明了一些变量,而且希望当这个函数结束时其中声明的变量依然存在,就可以将这些变量置于堆中。堆与栈相比,没有那么清晰的结构性。可以把堆看作是一“堆”小玩艺。程序可以在任何时刻向这个“堆”添加新的东西或者修改“堆”中已经有的东西。
用栈和堆来分析动态分配数组的原理: 根据栈的工作原理,编译器在编译时就必然能够确定每个栈帧有多大。 由于栈帧大小是预定的,因此无法声明一个大小可变的数组。 如: 以下代码就无法通过编译,因为arraySize是变量,而不是常量。
int arraySize = 8;
int myVaraibleSizedArray( arraySize ); // 不能通过编译
由于整个数组都要放在栈上,编译器需要准确地知道数组的大小(根据栈的工作原理,编译器在编译时必须要确定每个栈帧有多大。)所以不充许用变量来指定数组大小,如果通过使用动态内存(dynamic memory),把数组放在堆中(而不是栈中),就可以在运行时才指定数组的大小。
要动态分配一个数组,首先需要声明一个指针(pointer):
int * myVariableSizedArray;
myVariableSizeArray = new int[arraySize]; // 在堆中分配数组空间
这就会根据arraySize变量的大小来分配相应的内存。可以看到,指针变量任然位于栈中,而指针指向的数组则位于堆中。。
然而由于堆中变量占用的内存空间不会因为程序或函数结束而释放掉(在栈中变量则会因为程序或函数结束而释放内存),因此就需要人工来释放掉堆中的内存。用delete命令来释放内存.
delete [] myVariableSizeArray;