作者在初学值类型、引用类型时就一头雾水,相信大部分人也是一样的,现在回过头来总结一下。
说起值类型、引用类型这件事呀,那就得从头说起...
首先,我们可以将程序运行时的数据简单分为两类。一类是数据结构简单、单一,并且数据量不大的对象;另一类是数据结构复杂、数据量较大,并且通常需要在各函数体之间传递的对象。
又因为,数据都是存放在内存中的,这一点毋庸置疑。
针对第一类数据,我们希望读写速度能够尽量快,并且该类数据因为不需要再各函数体之间传递,所以也希望在程序不需要使用这些数据时能够自动释放这些数据所占的内存空间。
针对第二类数据,我们希望有一块内存能够长久的存放我们的数据,同时也希望能实现在不同函数体之间,能够快速的访问同一对象。
于是乎,聪明的程序员们发明了两个东西,一个名为栈内存,其特性是空间小、读写速度快、栈中数据先进后出,后进先出(读写速度快是因为,代码每进入到一个函数体中,就会提前为该函数体申请一块栈内存空间,用于存放函数体中变量数据的存储);
另一个名为堆内存,其特性是空间大、读写速度较慢、存入的数据在没有手动删除之前将存在至整个程序生命周期结束(读写速度慢是因为,所有程序都共享剩下的堆内存,而对于对堆内存中空间的申请需要由操作系统统一调度、协调各程序,所以显得慢)。
我们来实际推演一下。
想象程序正在一行行的执行代码,代码遇到函数调用时跳到该函数体中,函数体内代码执行完毕后又从该函数退出,如此反复...这个函数的调用、退出的方式其实便是:先进后出、后进先出(例如代码在进入Fun1()后,又在Fun1()中进入了Fun2(),Fun2()执行完毕后,是先由Fun2()退出,再到Fun1()退出)。大家可以惊讶的发现,这其实就是栈的特性。于是大佬们从此就这么定了,就把函数体内值类型数据本身存放在函数体申请的栈中,并且也把引用类型数据的地址也存放在栈中。
接下来,我们来把第二类数据套入到' 堆内存 '这个模型中。对于一些数据结构复杂且很有可能需要在各函数之间频繁传递且访问的数据,存于一个对程序而言空间大、可永久存储、可随时申请、随时释放的地方是最合适不过了。于是大佬们又决定,今后把这一类数据就存于堆内存中。程序通过地址访问该数据,而这个地址其实就是引用类型变量本身。这样定义后,只要在不同函数之间传递地址即可实现在不同函数中访问同一个对象。
所以,其实对于栈内存中的数据,程序员们并不需要管理其生命周期;而对于堆空间中的数据,需要手动释放,而对于C#而言,有CLR的垃圾回收机制帮忙自动回收后续没有再使用的堆内存中的数据;而对于C++而言则需要通过delete实现释放。
C++对于需要手动把值类型变成引用类型则通过new的方式(例如 int* a = new int(5),这时‘ 5 ’这个数据便存放在堆中,而a则成为范围‘ 5 ’数据的一个地址,也就光荣成为了引用类型变量)
C++默认更多值类型,C#默认更多引用类型
参考:
https://blog.csdn.net/weixin_53472920/article/details/119744114?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-119744114-blog-109323575.pc_relevant_aa&spm=1001.2101.3001.4242.1&utm_relevant_index=3
https://blog.csdn.net/ananlele_/article/details/109025276
标签:函数,C#,引用,C++,内存,类型,数据,混讲 From: https://www.cnblogs.com/ShawBlack/p/16997772.html