堆栈是计算机中很常见的一个概念,而数据结构中的堆栈和内存分配中的堆栈又存在着一些区别,下面将分别介绍数据结构中的堆栈和内存分配中的堆栈。
1. 数据结构中的堆栈
在数据结构中我们常说的堆栈其实指的是栈(Stack),而堆(Heap)和栈(Stack)都是一种数据项按序排列的数据结构。
1.1 数据结构中的堆(Heap)
在数据结构中,堆是一种经过排序的树形数据结构,每个结点都有一个值。如我们常说的最大堆、最小堆,所以堆在查找相关数据的时候不需要查看全部数据,可以根据结点值来进行查找。
1.2 数据结构中的栈(Stack)
栈是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取。这就如同我们要取出放在箱子里面底下的东西(放入的比较早的物体),我们首先要移开压在它上面的物体(放入的比较晚的物体)。
2. 内存分配中的堆栈
内存分配中的堆栈和数据结构中的堆栈概念有较大区别,内存分配的堆和栈都指的是内存空间,而Java中的堆栈和C中的堆栈也存在一些区别。
2.1 C中的堆栈
内存中的栈区处于相对较高的地址,
栈区是向下增长的,栈中分配局部变量空间。
堆区是向上增长的,用于分配程序员申请的内存空间。下面是一个经典的代码实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 1//main.cpp
2int a = 0; //全局初始化区
3int a = 0; //全局初始化区
4char *p1; //全局未初始化区
5main() {
6 int b; //栈
7 char s[] = "abc"; //栈
8 char *p2; //栈
9 char *p3 = "123456"; //123456\0在常量区,p3在栈上。
10 static int c = 0; //全局(静态)初始化区
11 p1 = (char *)malloc(10);
12 p2 = (char *)malloc(20);
13 //分配得来得10和20字节的区域就在堆区。
14 strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
15}
16
17
可以看出,栈是系统自动分配的,但是堆需要程序员自己申请。同时,栈空间是自动自动回收的,即数据值存在于程序运行周期中,运行过后就释放;但是堆空间上的数据需要程序员自己释放,如果不释放,可以一直访问。
2.1.1 堆和栈内存申请后系统的响应
**栈:**只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
**堆:**首先应该知道操作系统有一个记录空闲内存地址的链表,当
系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
2.1.2 堆和栈申请大小的限制
**栈:**在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是
栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。
堆的大小受限于计算机系统中有效的虚拟内存,由此可见,堆获得的空间比较灵活,也比较大。
2.1.3 堆和栈的存取效率
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
1
2
3
4
5
6
7
8
9
10
11 1void main()
2{
3char a = 1;
4char c[] = "1234567890";
5char *p ="1234567890";
6a = c[1];
7a = p[1];
8return;
9}
10
11
对应的汇编代码
1
2
3
4
5
6
7
8
9 110: a = c[1];
200401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
30040106A 88 4D FC mov byte ptr [ebp-4],cl
411: a = p[1];
50040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
600401070 8A 42 01 mov al,byte ptr [edx+1]
700401073 88 45 FC mov byte ptr [ebp-4],al```
8
9
堆和栈的区别可以引用一位前辈的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。比喻很形象,说的很通俗易懂,不知道你是否有点收获。
2.2 java中的堆和栈
如上图所示,当创建变量s时,会进入到栈内存,将Student s放入栈内存中,然后new一个堆内存空间来存储Student()对象,堆内存中有Student()对象的成员变量和成员方法,在实例化对象时可以对其进行赋值。
2.2.1 栈内存
栈内存首先是一片内存区域,存储的都是局部变量。程序运行的时候,方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。
2.2.2 堆内存
堆内存存储的是数组和对象,凡是new建立的都是在堆中,和C中的堆内存相同,Java中堆是不会随时释放,
但是在Java中有独特的垃圾回收机制,可以不定时的对堆进行自动回收,所以在Java中不需要程序员自己释放堆。
参考资料:
https://www.cnblogs.com/jiahuafu/p/8575044.html
https://blog.csdn.net/yingms/article/details/53188974
https://www.php.cn/faq/416802.html