多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 数据结构中的堆和栈 与 内存分配中的堆区和栈区 分析 在计算机领域,堆栈是一个不容忽视的概念,我们编写的C/C++语言程序基本上都要用到。但对于非常多的初学着来说,堆栈是一个非常模糊的概念。 ### 数据结构的栈和堆 首先在数据结构上要知道堆栈,虽然我们这么称呼它,但实际上堆栈是两种数据结构:堆和栈。 堆和栈都是一种数据项按序排列的数据结构。 **栈就像装数据的桶或箱子** 我们先从大家比較熟悉的栈说起吧。它是一种具有后进先出性质的数据结构,也就是说后存放的先取。先存放的后取。这就如同我们要取出放在箱子里面底下的东西(放入的比較早的物体),我们首先要移开压在它上面的物体(放入的比较晚的物体)。 **堆像一棵倒过来的树** 而堆就不同了。堆是一种经过排序的树形数据结构。每一个结点都有一个值。 通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大)。且根结点的两个子树也是一个堆。因为堆的这个特性,经常使用来实现优先队列,堆的存取是随意。这就如同我们在图书馆的书架上取书,尽管书的摆放是有顺序的。可是我们想取随意一本时不必像栈一样,先取出前面全部的书。书架这样的机制不同于箱子,我们能够直接取出我们想要的书。 ### 内存分配中的栈和堆 内存中的栈区处于相对较高的地址以地址的增长方向为上的话,栈地址是向下增长的。栈中分配局部变量空间。堆区是向上增长的用于分配程序猿申请的内存空间。另外还有静态 区是分配静态变量,全局变量空间的;只读区是分配常量和程序代码空间的;以及其它一些分区。 ![](https://box.kancloud.cn/3621d5d1f34e92a18233d57f41df5896_455x295.png) ## 预备知识—程序的内存分配 一个由C/C++编译的程序占用的内存分为下面几个部分 1、栈区(stack)— 由编译器自己主动分配释放 。存放函数的參数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap) — 一般由程序猿分配释放。 若程序猿不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的还有一块区域。 - 程序结束后有系统释放 4、文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放 5、程序代码区—存放函数体的二进制代码。 ## 堆和栈的理论知识 ### 申请方式 stack:由系统自己主动分配。 比如。声明在函数中一个局部变量 int b; 系统自己主动在栈中为b开辟空间 heap:须要程序猿自己申请。并指明大小,在c中malloc函数 ,如 ``` p1 = (char *)malloc(10); ``` 在C++中用new运算符,如 ``` p2 = (char *)malloc(10); ``` 可是注意p1、p2本身是在栈中的。 ### 申请后系统的响应 栈:仅仅要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 堆:首先应该知道操作系统有一个记录空暇内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空暇结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样。代码中的delete语句才干正确的释放本内存空间。 另外。因为找到的堆结点的大小不一定正好等于申请的大小。系统会自己主动的将多余的那部分又一次放入空暇链表中。 ### 申请大小的限制 栈:在Windows下,栈是向低地址扩展的数据结构。是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下。栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),假设申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是因为系统是用链表来存储的空暇内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。 堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比較灵活。也比较大。 ### 申请效率的比較 栈由系统自己主动分配,速度较快。但程序猿是无法控制的。 堆是由new分配的内存,一般速度比較慢。并且easy产生内存碎片,只是用起来最方便. 另外。在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆。也不是在栈是直接在进程的地址空间中保留一快内存。尽管用起来最不方便。可是速度快,也最灵活。 ### 堆和栈中的存储内容 栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可运行语句)的地址,然后是函数的各个參数。在大多数的C编译器中。參数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后,局部变量先出栈。然后是參数。最后栈顶指针指向最開始存的地址。也就是主函数中的下一条指令,程序由该点继续执行。 堆:通常是在堆的头部用一个字节存放堆的大小。 堆中的详细内容有程序猿安排。 ### 存取效率的比較 ``` char s1[] = "aaaaaaaaaaaaaaa"; char *s2 = "bbbbbbbbbbbbbbbbb"; ``` aaaaaaaaaaa是在执行时刻赋值的; 而bbbbbbbbbbb是在编译时就确定的。 可是,在以后的存取中。在栈上的数组比指针所指向的字符串(比如堆)快。 比方: ``` #include void main() { char a = 1; char c[] = "1234567890"; char *p ="1234567890"; a = c[1]; a = p[1]; return; } ``` 相应的汇编代码 ``` 10: a = c[1]; 00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 0040106A 88 4D FC mov byte ptr [ebp-4],cl 11: a = p[1]; 0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 00401070 8A 42 01 mov al,byte ptr [edx+1] 00401073 88 45 FC mov byte ptr [ebp-4],al ``` 第一种在读取时直接就把字符串中的元素读到寄存器cl中,而另外一种则要先把指针值读到edx中。在依据edx读取字符,显然慢了。 ### 小结 堆和栈的差别能够用例如以下的比喻来看出: 使用栈就象我们去饭馆里吃饭。仅仅管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的优点是快捷,可是自由度小。 使用堆就象是自己动手做喜欢吃的菜肴。比較麻烦。可是比較符合自己的口味,并且自由度大。