一般来说,程序就是与数据打交道,在执行某一功能的时候,将该功能所需要的数据加载到内存中,然后在执行完毕的时候释放掉该内存。
数据在内存中的存放共分为以下几个形式:
1、栈区(stack):由编译器自动分配并释放,该区域一般存放函数的参数值、局部变量的值等。
2、堆区(heap):由程序员分配和释放。若程序员不释放,程序结束时可能由操作系统回收内存空间。
3、寄存器区:用来保存栈顶指针和指令指针。
4、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。
5、文字常量区:常量字符串就是放在这里的,程序结束后由系统释放。
6、程序代码区:存放函数体的二进制代码。
函数参数和局部变量存放在栈中,当函数运行结束并且返回时,所有的局部变量和参数就都被系统自动清除掉,为的是释放掉他们所占用的内存空间。全局变量可以解决这个问题,但是全局变量永远不会被释放,而且全局变量被所有的类成员和函数所共享,所以它的值很容易被修改。
接下来讨论堆和栈的问题:
很多时候我们把堆和栈放在一起说,比如堆栈,其它他们是有区别的,至于为什么把他们混合在一起说,这个是历史问题,这就不追究下去了。
1、内存的申请方式上的不同:
(一)、栈:由系统自动分配内存,例如我们在函数中声明一个局部变量int a;那么系统就会自动在栈中为变量a开辟内存空间。
(二)、堆:需要程序员自己申请内存空间,因此也需要指明变量的大小。
2、系统响应的不同
(一)、栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存空间,否则将提示overflow,也就是栈溢出。
(二)、堆:系统收到程序申请空间的要求,会遍历一个操作系统用于记录内存空间地址的链表,当找到一个空间大于所申请空间的堆节点后,就会将该结点的内存分配给程序,然后在这块内存区域的首地址处记录分配的大小,这样我们在使用delete来释放内存的时候,delete才能正确地识别并删除该内存区域的所有变量。另外,我们申请的内存空间与堆结点上的内存空间不一定相等,这时系统就会自动将堆结点上多出来的那一部分空间回收到空闲链表中。
3、空间大小的不同
(一)、栈:在windows下,栈是一块连续的内存区域。是由系统预先根据栈顶的地址和栈的最大容量定义好,假如你的数据申请的内存空间超过栈的空间,那么就是提示overflow。因此,别指望栈能存储比较大的数据。
(二)、堆:堆是不连续的内存区域。各块区域由链表将它们串联起来,关于链表的知识(请学习数据结构)。这里只要知道链表将各个不连续的内存区域连接起来,这些串联起来的内存空间叫做堆,它的上限是由系统中有效的虚拟内存来定的。因此获得的空间比较大,而且获得空间的方式也比较灵活。
4、执行效率的 不同
(一)、栈:栈由系统自动分配内存,因此速度较快。但是程序员不能对其进行操作。
(二)、堆:堆是由程序员分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来很方便。
5、执行函数时的不同
(一)、栈:在函数调用时,第一个进栈的是被调用函数下一行的内存地址。其次是函数的参数,假如参数多于一个,那么次序是从右往左。最后才是函数的局部变量。
由于栈的先进后出原则,函数结束时正好与其相反,首先是局部变量先出栈,然后是参数,次序是从左往右,这时所有的变量都已出栈,指针自然地指到第一个进栈的那行内存地址,也就是被调用函数的下一行内存地址。程序根据该地址跳转到被调用函数的下一行自动执行。
(二)、堆:堆是一大堆不连续的内存区域,在系统中由链表将他们串联起来,因此在使用的时候必须由程序员来安排。它的机制是很复杂的,有时为了分配一块合适的内存空间,程序员需要按照一定的算法在堆内存中搜索可用的足够大的空间,如果没有满足条件的空间,那么就要向系统发出申请增加一部分内存空间,这样就有机会得到足够大的内存空间,然后将计算后的数值返回。显然,堆的运行效率要比栈低的多,而且也容易产生碎片。但是好处是可以储存相当大的数据,并且一些细节也可以有程序员来安排。