严格来说,堆(heap)和栈(stack)是两种数据结构,但:
ʅ(‾◡◝)ʃ,飞哥也很无奈,同学们就根据上下文理解吧,不用太较真……
PS:致敬https://stackoverflow.com/,此外,源栈的“栈”,知道啥意思了吧?堆可以有两种理解:
简单的说,就是数据散乱的分布,没有任何组织和规律,就像一堆货物堆在那里一样:
演示:堆的形成
同时,产生很多存储碎片:零星的小型的无法/很难被使用的存储空间(无论是内存还是外存都一样)。
如何对付存储碎片?
看起来像一颗树,但和树有区别:
简单的说,是一种“先进后出”的数据结构。用栈来存放数据:
装子弹的弹匣就是一种典型的“栈”:
子弹被击发的顺序是和被压入的顺序相颠倒的:最后压入的一颗子弹会被最先击发,而最先压入弹匣的子弹只有等所有上面的子弹打完之后才能被击发。
我们将其进行抽象,就可以得到下面这样的栈的数据结构图:
说明:左图top不移动,右图bottom不移动
运动是相对的,所以上面左右两种图形都是可以的。右图可能更直观,它和弹匣结构一一对应:
@想一想@:每一次数据出栈,我们都需要将数据“往上推”吗?怎么推?
提示:运动是相对的,为什么一定要移动数据呢?能不能移动top指针啊?(复习:为什么需要引用类型)
这是因为函数的嵌套调用就是一个的栈的使用过程!
我们的源代码经过编译/解释,最终在计算机上以机器码的方式运行,这时候就没有函数的概念,所有的代码都被转换成一条一条的指令,CPU依次逐个的运行这些指令就可以了——就像手枪击锤一个一个的击发子弹一样。(复习:冯·诺依曼结构)
但是,内存资源是有限的,所以我们不能把全部代码一次性的加载进来,只能分批次的加载执行:就像装一个弹匣的子弹,打完了再装一弹匣再打一样。装这些代码容器最好就是栈。
我们来看一下这些代码:
static void Main(string[] args) { int average = getAverage(98,85,16); Console.WriteLine(average); } static int getAverage(double sql, double csharp, double js) { double score = (sql + csharp + js) / 3; return Convert.ToInt32(score); }
是不是?Main()先进后出,Convert.ToInt32()后进先出,整个过程,和栈的完美契合。
理解:
最后,在方法getAverage()中设上断点,运行,查看Call Stack窗口(如果找不到的话,导航栏:Debug->Windows->Call Stack打开)
这是一个非常有用的调试技巧,让我们可以知道当前代码是如何被一个一个的方法调用的。知道它为什么被称之为Call Stack(调用堆栈),而不是Call Methods(调用方法)了吧?
是一种先进先出的数据结构。
主要用于“削峰”,以避免拥堵,在所谓“高并发大流量”(比如秒杀抢购)的场景中广泛应用:
PS:是不是就和生活场景里的“排队”一样?这不仅仅是文明礼貌,而是整体社会运行秩序的保障。
多快好省!前端后端,线上线下,名师精讲
更多了解 加: