今天看到了这个才发现我对堆&栈的印象是有一些偏差的:
int *p=new int[5]
这句代码里面,p是在栈里面,而数组是在堆里面。
而我以为栈里面只放调用的函数。。naive了
(下面的内容来源于网络。。如果有不正确的可以讨论)
首先声明&定义&栈:
C语言中:如
extern User user;extern int a;
就是声明,变量的声明就是仅仅告诉编译器,声明的变量的存在,要预留一点空间,但并不为其分配内存.定义就是声明这个变量并真正在内存(堆或栈中)为此变量分配空间.
从编译原理上来说,声明就是仅仅告诉编译器,有个某类型的变量会被使用,但是编译器并不会为它分配任何内存.而定义就是不仅知道某类型的变量会被使用,并且已经为其分配了内存.
因为 在编译的时候,编译器先处理一些特殊数据(宏定义,函数的声明,变量的声明),在这个过程中,编译器 通过声明 可以预测整个定义需要的内存大小,并且 把这些大小预留起来,留给定义的时候使用(因为虽然定义,但是在 main函数里面没有调用,一样的不分配内存)。 如果不事先声明,直接定义,有可能会造成系统崩溃,出现内存不足,不能分配。 然而现在内存都是1G左右了,所以一般空间都足够,所以你也可以不声明直接定义函数。 但是一旦内存紧张,就会出错,而且程序简洁性就很低。(不看到定义,还不知道原来还定义了这么一个函数!!!)
在C++中,如
short a;int b;
这些是定义,也就是说在栈上分配了内存;
而对于对象,比如自定义的类:User user;
也就是说在栈上分配了引用或句柄,没有具体分配对象的内存;
而User *puser
就需要puser=new User();
才会真正为对象在堆上分配空间.
在C和C++中,extern User user
就是声明,并没有分配内存空间.
内存中的各个区块的作用:
- 栈(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 堆(heap) :一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。malloc和new等操作实际上就是在堆中申请内存,对象使用完后要手动释放,否则只能等待程序结束时由系统回收,会产生内存泄漏。
- 全局区(静态区)(static):全局变量和静态变量是存储在一起的,初始化过的全局变量和静态变量在同一块区域,未初始化的全局变量和静态变量存放在一块相邻的区域内。此区域由系统在程序结束后释放。
- 文字常量区:常量字符串存放于此,在程序结束后由系统释放。字符常量就是像这样的 char* str=”abc”;其中的”abc”。在实际情况中,是会复用的,比如变量a和b都赋值为”abc”则实际上他们指向同一块地址。
然后堆/栈的区别:
- 栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:
分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
- 堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考 数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的 内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
生长方向:
- 对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;
- 对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;
由于没有专门的系统支持,效率很低;
由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。