堆(heap)和栈(stack)的理解和区别

数据结构中的堆和栈

在数据结构中,栈是一种实现先进后出的存储结构。
假设给定栈 S = (a0,a1,…., an-1),则称a0 为栈底,an-1 为栈顶,进栈顺序按照a0,a1 … ,an-1 进栈;出栈则依据“先进后出”的规则进行,an-1 先出栈,依次再到a0

在实际编程中,可以通过两种方式实现栈:

  • 数组,称为静态栈

  • 链表,称为动态栈

而在数据结构中,堆则是通过排序的树形数据结构,常用来实现优先队列等。假设有一个集合 K={k0,k1,…,kn-1},把它的所有元素按完全二叉树的顺序存放在一个数组中,并且满足:

则称这个集合 K 为最小堆(或者最大堆)。

由此可见,堆是一种特殊的完全二叉树。其中,节点是从左到右填满的,并且最后一层的树叶都在最左边(即如果一个节点没有左儿子,那么它一定没有右儿子);每个节点的值都小于(或者都大于)其子节点的值。

内存分配的堆和栈

C、C++中内存分配如下三种形式:
  1. 静态存储区域分配:它由编译器自动分配和释放,即内存在程序编译时就已分配,这块内存在程序运行期间一直存在,知道程序程序运行结束时才释放,如全局变量static变量

  2. 在栈上分配:它同样也是由编译器自动释放和分配的,即在执行函数时,函数的局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元将被自动释放。需要注意的是栈内存分配运算置于处理器的指令集中,运行效率高,但分配的内存容量有限。

  3. 在堆上分配:即动态内存分配,它是由程序员手动完成申请和释放的。即程序在运行的时候由程序员使用内存分配函数(如malloc)来申请多少内存,使用完后再由程序员使用释放函数进行内存释放。动态内存的生存期是由程序员自己决定的。需要注意的是,在堆分配了内存空间,就必须及时释放它,以免造成内存泄露。

C、C++中程序编译时分为5大存储区:
  • 栈区:由编译器在需要的时候分配,在不需要的时候自动清除变量存储区。里面的变量通常时局部变量和参数。
  • 堆区:就是程序员通过使用函数new、alloc申请分配的内存块,它的释放由程序员自己控制。如果程序结束后还没释放掉,操作系统会自动回收。
  • 全局/静态区:全局变量和静态变量的存储是放在一起的。已初始化的全局变量和静态变量放在一块区域,未初始化的全局变量和静态变量放在相邻的一块区域,在程序编译时分配。
  • 常量区:存放常量字符串。
  • 程序代码区:存放函数体(类的成员函数、全局函数)的二进制代码。

内存分配的堆与栈的区别

分配与释放方式

栈内存分配是由编译器自动分配与释放的。有两种方式:静态分配与动态分配

  • 静态分配,是由编译器自动完成的,例如局部变量的分配,同时其生存周期是函数运行的过程中开始到结束自动释放,并不可以再次访问。
  • 动态分配由alloca 函数分配,但栈的动态分配与堆不同,它的动态分配是由编译器进行释放。

堆内存分配则是完全由程序员手动申请与释放的。程序运行中由程序员使用内存分配函数来申请任意多少内存,使用完在由程序员使用内存释放函数释放内存。

分配效率问题

堆的分配效率要比栈低的多
栈是机器系统提供的数据结构,计算机会在底层对对栈提供支持,例如,分配专门的寄存器存放栈的地址,压栈出栈都有专门的执行指令,这就决定了栈的效率比较高。一般而言,只要栈的剩余空间大于所申请空间,系统就将为程序提供内存,否则将报异常提示栈溢出。
而堆不同,是由C、C++函数库提供的,机制也相对复杂。例如,为了分配一块堆内存,首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。而对于大多数系统,会在这块内存空间的首地址处记录本次分配的大小,这样,代码中的 delete 语句才能正确释放本内存空间。另外,由于找到的堆节点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。很显然,堆的分配效率比栈要低得多。

分配的碎片问题

对堆来说,频繁分配和释放(malloc / free)不同大小的堆空间势必会造成内存空间的不连续,从而造成大量碎片,导致程序效率降低;

而对栈来讲,则不会存在这个问题。

申请大小限制

由于操作系统是通过链表来存储空闲的内存地址(内存是不连续的)的,链表的遍历方向是由低地址向高地址执行的。因此,堆内存的申请大小受限于计算机系统中有效的虚拟内存大小
而栈不同,它是一块连续的内存区域,其地址是向下增长的,向内存地址减小的方向增长。由此可见,栈顶的地址和栈的最大容量一般都是由系统预先规定好的,如果申请的空间大于栈的剩余空间时,会报溢出错误。由此可见,相对于堆,能够从栈中获得的空间较小

存储的内容

对栈而言,一般用于存放函数的参数局部变量
对堆而言,具体存储的内容是根据程序员需要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wahkim

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值