Redian新闻
>
堆和栈它们的内涵有多深

堆和栈它们的内涵有多深

公众号新闻

前言

作为一名程序员,许多同学会把堆和栈这两个字,变成一个词放在一起,其实这是错误的。

堆(heap)

堆是一个内存空间,这个内存可以由程序员分配和释放,当然部分语言自带 GC( Garbage Collection 垃圾回收),部分堆内存可以由 GC 回收。这里千万要注意,这里说的「堆」数据结构 里面说的「堆」不是同一个概念,大家千万不要混淆。

堆是程序在运行的时候请求操作系统分配给自己内存。由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率相对栈来说略低。但是堆的优点在于,编译器不必知道要从堆里分配多少内存空间,也不必知道存储的数据要在堆里停留多长的时间,因此用堆保存数据时会得到更大的灵活性。所以为达到这种灵活性,在堆里分配存储空间时会花相对更长的时间,这也是效率低于栈的原因。

栈(stack)

栈是由编译器自动分配和释放的,存放函数的参数值,局部变量的值等。也请注意,这里说的「栈」不是 数据结构 中的「栈」,大家千万不要混淆。这里请注意,栈是由由系统自动分配。

栈的优势是存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
说的再通俗点  
使用栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就像是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

实际应用

看完上面一段文字,大多数同学自然就懂了,而不懂的,还是一脸懵。这些东西我知道了有什么实际意义?比如栈既然是系统分配和释放,我干预不了,我知道它干什么?那这里就以编程语言 C#(C Sharp)来举例说明。也可以看相应的 👉 LeetBook
堆、栈的实际意义

堆、栈,算是相对底层一些的内容,大部分的语言其实这部分内容都是类似的。如果你不会 C# 语言也能看懂~。

我们都知道,C# 的数据类型有两大类型 —— 值类型引用类型。大致结构如下图:

值类型我们看到包含内置值类型、用户定义的值类型和枚举。枚举就不用说了,内置值类型指的是 int、float、bool、double 等等。

用户定义的值类型指的就是 struct。引用类型大概我们学 C 语言的时候学到的指针类型(C# 做了封装,所以我们看不到 int * 这类的代码,当然可以使用 unsafe 关键字,这里就不多赘述)、接口类型、用户自定义的类(class),数组等。
类型的含义
这里为什么要解释这两个大的类型?首先是因为值类型是放在栈上的,值类型变量声明后,不管是否已经赋值,编译器为其分配内存。然而引用类型当声明一个变量时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,这时会分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。

通俗一点,堆上存放的是我们声明出来的实例对象,当我们需要访问这个实例对象时,我们找到它的方法是先找到变量对应在栈上的内存,然后通过栈上存放的数据,我们才能找到其真正的实例在堆的任意位置。


代码

class A{    public A(int x){        X = x;    }
public int X { get; set; }}
static void Main(string[] args){ int m = 1; int n = m; m = 2; Console.WriteLine($"m = {m}, n = {n}");
A a = new A(1); var b = a; a.X = 2; Console.WriteLine($"a.X = {a.X}, b.X = {b.X}");}

我们会发现输出结果是

m = 2, n = 1a.X = 2, b.X = 2

我们发现 m 和 n 的值是分别独立的,而 a 和 b 修改其中一个会修改两个值。这是因为 m 和 n 都是分配在栈上的,而 a 和 b 虽然也是在栈上,但是其只是存储的堆上的一个地址索引,我们不管通过 a 还是 b 索引到的堆上的内存都是同一份。

我们再来看值类型和引用类型,在栈上的数据访问快,在堆上的数据访问相对慢,因此,当我们开发过程中,通过需求,比如底层的一些不变数据,完全可以有 struct 来实现,因为其不会为 null,符合值类型的要求,而且我们经常访问会更快一些。

我们看上图发现,值类型和引用类型都是继承自 Object ,也就是说:
Object a = 1;int b = (int)a;

这两个都是合法语句。但是这里面隐藏了一个非常常见的一个现象,那就是装箱和拆箱。

  • 什么是拆装箱?
装箱实际上是将值类型转换为引用类型,而拆箱是将引用类型转换为值类型。再透彻点,实际上装箱拆箱就是栈内存和堆内存的来回拷贝和赋值,这不仅仅浪费性能,而且还会造成没必要的 GC。因此,能避免 “装箱”、“拆箱” 操作的就尽量避免。除上面所说的这些数据类型,还有一个写代码必不可少的,那就是函数。
函数是如何调用的?实际上函数调用的参数是通过栈空间来传递,在调用过程中会占用线程的栈资源。

当我们使用递归算法时,每次虽然调用的是同一个函数,但是会在栈中占用一个空间,只有走到最后的结束点后函数才能依次退出,而未到达最后的结束点之前,占用的栈空间一直没有释放。因此如果递归调用次数过多,就可能导致占用的栈资源超过线程的最大值,从而导致栈溢出,这也是为什么一定要尽量少用递归的原因。


写在最后

上面说了很多,侧面说明了堆、栈的应用的优缺点,平时应用中需要更加了解我们写的代码都“干了什么”,这样才有可能写出更高效、更可靠的代码。对于一名程序员来说,不管未来编程语言发展的多么容易上手,一定不要忘了学习计算机的基础知识,哪怕是先学会了编程再返回来补习这些知识。祝大家工作顺利、评论留言少 BUG !点赞转发不脱发!


BY / 

本文作者:力扣

编辑&版式:Janson

声明:本文归“力扣”版权所有,如需转载请联系。


点个在看,少个 bug

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
马化腾谈PCG内部改革不客气:活都活不下去,周末还休闲打球,留给它们的时间不多了我走访了很多深圳中小学,几乎每间教室都这样:大白天开着灯,然后拉着窗帘中金:中国式现代化的经济内涵 ——二十大报告简评它们的搞笑喜感瞬间,承包了我这一年的笑点创新一习话丨“实现高质量发展,必须实现依靠创新驱动的内涵型增长”ML如何做科学发现?牛津大学268页博士论文详述科学机器学习内涵你想不到水有多深奈雪牵手乐乐茶,解决不了它们的模式难题文科老留,再也不用担心被解雇了 (中)一充再充、终端不兼容,电视会员的陷阱有多深?奥迪新海报,内涵了一把宝马、奔驰!大佬们的内部讲话,左边是大棒,右边是胡萝卜人到中年-对财富承担责任白云升起的地方:尼加拉大瀑布实木沙发实为水泥,家具市场的“水”到底有多深清朝康乾盛世时期,其他国家在做什么?它们的发展如何?你的内裤该“换季”了,试了32条才找到「不夹臀」的内裤,暖呼呼,提臀超性感消失的童年:我们的孩子,正在被它们“洗脑”羽绒服新国标不看“含绒”看“绒子”,它们的区别在哪?普京这次特殊的会见,暴露出很多深层次的问题深度解析,唐僧肉长生不老的本质与内涵人间宝藏到底能挖多深?这台机器亮了!丨 图图科幻在新加坡到处都是猫,它们的耳朵都少了一角,你知道是为什么吗?小毕娶了日本媳妇(二)马克谈天下(330) 聊聊公投那些事虚假实习、PUA学生,装Gay……留学生找工作的坑有多深?你想象不到!越来越贵的羽绒服,水有多深?香蕉,怎么成了全球流行的内涵符号?国海研究 | 我们的行业,我们的2022,我们的2023!盘点美国的“南方哈佛”的5所大学,它们的热门申请专业是什么?奥迪发了一张内涵海报,奔驰宝马至今不敢回应?【宏观经济】中国式现代化的精神内涵与战略部署—学习二十大报告精神上海、杭州先后发现XBB、BQ.1毒株,Cell论文揭示它们的免疫逃逸有多强不要低估它们的下作张艺兴内涵前队友?沈腾瘦身成功大变样?如菊姐厌烦老公?糊咖男星很狂妄?
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。