【C语言】数据结构和内存中的堆和栈

致读者:

首先我们可以先看看这篇Blog : 动态内存分配malloc,realloc,calloc区别


一、数据结构中的栈与堆:


堆和栈在数据结构中是两种不同的数据结构,两者都是数据项按序排列的数据结构。

  • 栈:像是装数据的桶或者箱子

    栈是大家比较熟悉的一种数据结构,它是一种具有后进先出的数据结构,也就是说后存放的先取,先存放的后取,这就类似于我们要在取放在箱子底部的东西(放进去比较早的物体),我们首先要移开压在它上面的物体(放入比较晚的物体)。

  • 堆:像是一颗倒立的大树

    堆是一种经过排序的树形数据结构,每个节点都有一个值。通常我们所说的堆的数据结构是指完全二叉树。堆的特点是根节点的值最小(或最大),且根节点的两个树也是一个堆。由于堆的这个特性,常用来实现优先队列,堆的存取是随意的,这就如同我们在图书馆的书架上取书,虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书,书架这种机制不同于箱子,我们可以直接取出我们想要的书。


二、内存分配中的堆和栈:


内存中:

  • 栈区处于相对较高的地址,以地址的增长方向为上的话,栈地址是向下增长的,栈中分配局部变量空间.空间是自动分配自动回收的。
  • 堆区是向上增长的,用于分配程序员申请的内存空间,需要程序员自己回收释放,否则易造成内存泄漏。如图:
    【C语言】数据结构和内存中的堆和栈

区别:

  • 1.申请释放方式

在申请内存和释放内存方式方面,堆和栈有着很大的不同:

  • 栈是编译器自动申请的,例如在主函数里面,要声明一个int变量a,那么编译器就自动开辟一块内存存放变量a,空间在程序结束的时候由系统或者编译器自动释放。
  • 堆则不相同,是由程序员手动申请的,只要程序员感觉程序此处需要用到多大的内存空间,那么就使用malloc或者new来申请固定大小的内存使用,其空间是在程序结束前由程序员手动使用free/delete释放,或者忘记手动释放,由系统在程序结束的时候自动回收。
  • 2.申请后系统的相应
  • 栈,只要栈剩余的空间大小比申请的空间大,系统就自动为其分配空间,否则就会报错说明栈空间溢出。
  • 堆,首先要知道操作系统中有一个存放空闲存储块的链表,当程序员申请空间的时候,系统就会遍历整个链表,找到第一个比申请空间大的空闲块节点,系统会将该空闲块从空闲链表中删除,分配给程序,同时系统会记录这个空闲块的首地址和申请的大小,当程序员使用free/delete释放该空间的时候能够找到该存储区。另外,申请的空间不一定与找到的空闲块大小相同,多出来剩余的空闲区会被系统重新添加到空闲链表中。
  • 3.申请的限制
  • 栈,是一种向低地址扩展的数据结构,并且是连续的存储空间,所以栈顶和栈的最大容量是固定的,在windows下,栈的最大容量是2m或者是1m,是在编译的时候就已经确定的,当申请空间大于栈的剩余空间的时候,就会报错说明overflow,所以栈能够申请的空间是比较有限的。
  • 堆,是一种向高地址扩展的数据结构,并且是不连续的,因为系统采用的是链表的方式存放空闲存储块,当然是不连续的,链表的遍历方向是由低向高的,所以堆能够申请的空间的大小其实等同于整个系统的虚拟内存,只要还有内存空间,那么堆就能够不受限制的申请空间,这种方式比较灵活,申请空间也较大。
  • 4.申请效率的比较
  • 栈,因为栈空间的申请是由系统自动完成的,所以速度快,但是不受程序员控制。
  • 堆,空间的申请是由malloc或new来完成的,实现起来较慢,能够产生内存碎片,但是使用起来方便。
  • 5.存放内容
  • 栈,栈存放的内容,一般来说是函数地址和相关参数。当主函数要调用一个函数的时候,要对当前断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,然后是调用函数的参数,一般情况下是按照从右向左的顺序入栈,之后是调用函数的局部变量,注意静态变量是存放在全局内存区,是不入栈的;出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。
  • 堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来完成的。

补充:


全局变量、局部变量、全局静态变量、局部静态变量的区别。


要从分配内存的位置和作用域入手来解释。

  • 全局变量,分配的内存在静态存储区内存上面,其作用域是全局作用域,也就是整个程序的生命周期内都可以使用,同时,有些程序并不是由一个源文件构成的,可能有许多个源文件构成,全局变量只要在一个文件中定义,就可以在其他所有的文件中使用,当然,必须在其他文件使用extern关键字声明该变量。
  • 局部变量,分配内存是分配在栈存储区上的,其作用域也只是在局部函数内,在定义该变量的函数内,只要出了该函数,该局部变量就不再起作用,该变量的生命周期也只是和该函数同在。
  • 全局静态变量,分配的内存与全局变量一样,也是在静态存储内存上,其生命周期也是与整个程序同在的,从程序开始到结束一直起作用,但是与全局变量不同的是,全局静态变量作用域只在定义它的一个源文件内,其他源文件不能使用它。
  • 局部静态变量,分配的内存也是在静态存储内存上的,其第一次初始化后就一直存在直到程序结束,该变量的特点是其作用域只在定义它的函数内可见,出了该函数就不可见了。

结语

任何你不努力的理由,都可能成为你成功路上的火焰山!