【摘要】
堆和栈,即是数据结构,又是分配存储空间的不同方式。在数据结构上。堆是树型层次结构,结点按keyword次序排列,经常使用的堆为二叉堆;栈是一种先进后出的数据结构。在内存分配上的堆和栈,首要差别在于申请方式不同。其次在存取速度、存储空间的大小、存储内容(一定要记住,栈中是第一条可运行语句地址。然后是各个參数。堆中头部是堆的大小描写叙述。之后有程序猿自己安排)、内存中的相对位置和系统相应的响应上都各有自己差别。在C语言 的学习过程中,堆和栈即是基础也是重点。
【正文】
堆栈是一个非常模糊的概念,堆栈:一种数据结构?一个在程序执行时用于存放的地方?这可能是非常多刚開始学习的人的认识,由于,我以前就是这么想的和汇编语言中的堆栈一词混为一谈。
数据结构中的堆栈
实际上堆栈是两种数据结构:堆和栈。堆和栈都是一种数据项按序排列的数据结构。
栈是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取。
这就如同我们要取出放在箱子里面底下的东西(放入的比較早的物体)。我们首先要移开压在它上面的物体(放入的比較晚的物体)。而堆就不同了,堆是一种经过排序的树形数据结构。每一个结点都有一个值。通常我们所说的堆的数据结构。是指二叉堆。
堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。因为堆的这个特性。经常使用来实现优先队列,堆的存取是随意。
这就如同我们在图书馆的书架上取书。尽管书的摆放是有顺序的,可是我们想取随意一本时不必像栈一样,先取出前面全部的书,书架这样的机制不同于箱子。我们能够直接取出我们想要的书。
可是,重点并不在数据结构的堆和栈,之所以要说数据结构的堆和栈是为了和堆区和栈区差别开来。
内存分配中的堆栈
以下就说说C语言程序内存分配中的堆和栈,这里有必要把内存分配也提一下。大家不要嫌我啰嗦。普通情况下程序存放在Rom(仅仅读内存,比方硬盘)或Flash中,执行时须要拷到RAM(随机存储器RAM)中执行。RAM会分别存储不同的信息,例如以下图所看到的:
内存中的栈区处于相对较高的地址。栈地址是向下增长的,栈中分配局部变量空间,堆区处于相对较低的地址。是向上增长的用于分配程序猿申请的内存空间。
另外还有静态区是分配静态变量,全局变量空间的。仅仅读区是分配常量和程序代码空间的。以及其它一些分区。
来看一个网上非常流行的经典样例:
main.cpp int a = 0; //全局初始化区 char *p1; //全局未初始化区 main() { int b; //栈 char s[] = "abc"; //栈 char *p2; //栈 char *p3 = "123456"; //123456\0在常量区,p3在栈上。 static int c =0。 //全局(静态)初始化区 p1 = (char *)malloc(10); //堆 p2 = (char *)malloc(20); //堆 }
内存分配中堆和栈的差别与联系
1. 申请方式不同(首要差别)
栈(英文名称是stack)是系统自己主动分配空间的。比如我们定义一个 char a;系统会自己主动在栈上为其开辟空间。
堆(英文名称是heap)是程序猿依据须要自己申请的空间。比如malloc(10);开辟十个字节的空间。
因为栈上的空间是自己主动分配自己主动回收的,所以栈上的数据的生存周期仅仅是在函数的执行过程中,执行后就释放掉,不能够再訪问。而堆上的数据仅仅要程序猿不释放空间,就一直能够訪问到,只是缺点是一旦忘记释放会造成内存泄露。
2. 申请后系统的响应
栈:仅仅要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空暇内存地址的链表,当系统收到程序的申请时。会遍历该链表。寻找第一个空间大于所申请空间的堆结点,然后将该结点从空暇结点链表中删除,并将该结点的空间分配给程序。另外,对于大多数系统。会在这块内存空间中的首地址处记录本次分配的大小。这样,代码中的 delete语句才干正确的释放本内存空间。另外,因为找到的堆结点的大小不一定正好等于申请的大小。系统会自己主动的将多余的那部分又一次放入空暇链表中。也就是说堆会在申请后还要做一些兴许的工作这就会引出申请效率的问题
3. 申请效率的比較
栈由系统自己主动分配,速度较快。但程序猿是无法控制的。
堆是由new分配的内存。一般速度比較慢,并且easy产生内存碎片,只是用起来最方便。
4. 堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址。然后是函数的各个參数。
在大多数的C编译器中,參数是由右往左入栈的。然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后。局部变量先出栈,然后是參数,最后栈顶指针指向最開始存的地址,也就是主函数中的下一条指令。程序由该点继续执行。
堆:通常是在堆的头部用一个字节存放堆的大小。堆中的详细内容有程序猿安排。
5. 存取效率的比較
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
6. 申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),假设申请的空间超过栈的剩余空间时。将提示overflow。
因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。
这是因为系统是用链表来存储的空暇内存地址的。自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见。堆获得的空间比較灵活。也比較大。
最后引用一位前辈的比喻来结束堆和栈的差别,使用栈就象我们去饭馆里吃饭,仅仅管点菜(发出申请)、付钱、和吃(使用)。吃饱了就走。不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的优点是快捷,可是*度小。
使用堆就象是自己动手做喜欢吃的菜肴。比較麻烦,可是比較符合自己的口味。并且*度大。比喻非常形象,说的非常通俗易懂。我不知道,如果你是一个小的收获。
版权声明:本文博主原创文章,博客,未经同意不得转载。