定义
内存四区包含静态区、代码区、堆区、栈区
静态区
全局变量和静态变量存储在静态区
堆区
malloc的变量放在堆区,堆区一般由程序员分配释放。若程序员不释放,程序结束时可由操作系统回收。其分配方式类似于链表,在内存中的分布不是连续的,它们是不同区域的内存块通过指针链接起来的。一旦某一节点从链中断开,我们就要人为的将断开的节点从内存中释放。
- 堆的增长方式
由低地址向高地址栈区
局部变量存在栈区,由编译器自动分配释放,存放函数的参数值、局部变量的值。其操作方式类似数组,它的内存分配是连续分配的,即分配的内存是一块连续的内存区域。当声明变量时,编译器会自动接着当前栈区的结尾继续分配内存。
- 栈的增长方式
由高地址向低地址,函数参数载入时由右向左常量区
常量字符串存放于此,程序结束后由系统自动释放
代码区
存放函数体的二进制代码
一个正常的程序在内存中通常分为程序段、数据段和堆栈三部分。程序段放着程序的机器码、只读数据。这个段通常是只读,对它的写操作是非法的。数据段放的是程序中的静态数据,动态数据则通过堆栈来存放。
内存低端——-
程序段
数据段
堆 栈
内存高端——-
堆栈是内存中的一个连续的块,一个叫堆栈指针的寄存器(SP)指向堆栈的栈顶,堆栈的底部是一个固定地址。堆栈有一个特点就是,后进先出。也就是说,后放入的数据第一个取出。它支持两个操作,PUSH和POP。PUSH是将数据放到栈的顶端,POP是将栈顶的数据取出。
在高级语言中,程序函数调用、函数中的临时变量都用到堆栈。为什么呢?因为在调用一个函数时,我们需要对当前的操作进行保护,也为了函数执行后,程序可以正确的找到地方继续执行,所以参数的传递和返回值也用到了堆栈。
通常对局部变量的引用是通过给出它们对SP的偏移量来实现的,另外还有一个基址指针(FP,在Intel芯片中是BP),许多编译器实际上是用它来引用本地变量和参数的。
通常,参数的相对FP的偏移是正的,局部变量是负的。当程序中发生函数调用时,计算机做如下操作:首先把参数压入堆栈;然后保存指令寄存器(IP)中的内容,做为返回地址(RET);第三个放入堆栈的是基址寄存器(FP);然后把当前的栈指针(SP)拷贝到FP,做为新的基地址;最后为本地变量留出一定空间,把SP减去适当的数值。
堆和栈的基础知识
申请方式
- 栈
系统自动分配,声明局部变量int b;
系统自动在栈中为b分配空间 - 堆
程序员自己申请,并指明大小,在C中使用malloc函数,如p1 = (char *)malloc(10);
在C++中使用new运算符。但是,p1和p2本身是在栈中存储的。申请后系统的响应
- 栈
只要栈的剩余空间大于所申请的空间,系统就为程序提供内存,否则将异常提示栈溢出 - 堆
在操作系统中,有一个记录空闲内存地址的链表。当系统收到程序申请内存空间时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,对大多数系统来说,会在这块内存空间的首地址处记录本次分配的大小。这样,代码中的delete语句才能释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统自动会将多余的那部分重新放入空闲链表中。申请大小的限制
- 栈
在Windows中,栈是向低地址扩展的数据结构,是一块连续的内存区域,即栈顶的地址和栈的最大容量是系统预先规定好的。在Windows下,栈的大小是2M(它是编译时就确定的常数)。若申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 - 堆
堆是向高地址扩展的数据结构,是不连续的内存区域。由于系统是用链表来存储空闲的内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆得大小受限于计算机系统中有效的虚拟内存。由此可知,堆获得的空间比较灵活,也比较大。申请效率的比较
- 栈
由系统自动分配,速度较快,但程序员无法控制。 - 堆
用户由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来方便。
在Windows下,最好的方式是用VirtualAlloc分配内存,它不在堆,也不在栈,是直接在进程的地址空间中保留一块内存。虽然用起来最不方便,但是速度快,也最灵活。
堆和栈的存储内容
- 栈
在函数调用时,第一个进栈的是主函数中被调函数的下一条可执行语句的地址。然后是函数的各个参数,在大多数C编译器中,参数是由右往左入栈的,接着是函数中的局部变量。其中,静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是函数参数,最后是栈顶指针指向最开始存的地址,即主函数中的下一条指令,程序由该点继续往下执行。 - 堆
一般是在堆得头部用一个字节存放堆得大小,堆中具体内容由程序员安排。存取效率的比较
|
|
aaaaaaaa是在运行时进行赋值的,bbbbb是在编译时就确定了的。但是,在以后的存取中,在栈上的数组比指针所指向的字符串(如堆)快。
|
|
对应的汇编:
第一种在读取时直接将字符串中的元素读到寄存器cl中,而第二种则先把指针值读取到edx中,再根据edx读取字符串。
总结
- 堆和栈的区别:
使用栈就如去饭店吃饭,只需点菜(申请内存),付钱并吃(使用),不必做扫尾等工作,好处是快捷,但自由度小;
使用堆就如自己做菜,做自己想吃的,自由行大;
这里的堆实际指满足堆性质的优先队列的一种数据结构,第一个元素有最高的优先权;栈实际上是满足先进后出的性质的数据结构。