深入理解值类型和引用类型的存储

目录

内存

存储

1)栈区

2)堆区

C#的编译过程

1)源代码

2)公共语言规范(Common Language Specification,CLS) 编译

3)通用中间语言(Microsoft Intermediate Language,CIL或MSIL)

4)公共语言运行库(Common Language Runtime,CLR)编译

5)机器码 (0、1)

局部变量

值类型与引用类型

1)在栈中声明

2)在堆中声明

总结

拆装箱

1)装箱

2)拆箱


       今天来进一步学习值类型和引用的存储,加深对它们的理解,区分值类型和引用类型可以查看【一文读懂】C#区分值类型和引用类型

 

内存

       首先来了解一下内存,内存是CPU与其它外部存储器交换数据的桥梁,用于存储正在执行的程序与数据,即数据必须加载到内存才能被CPU处理。

 

存储

       程序在运行时,CLR将申请的内存空间从逻辑上进行划分。分为栈区和堆区。

1)栈区

       空间小,读取速度快。用于存储正在执行的方法,分配的空间叫做栈帧。栈帧中存储方法的参数和变量等数据。方法执行完毕后,对应的栈帧将被清除。

2)堆区

       空间大,读取速度慢。用于存储引用类型的数据。

 

C#的编译过程

       上述提到的CLR的与C#的编译过程有关,C#的编译过程可以概括为:源代码→CLS编译→通用中间语言(CIL)→CLR编译→机器码 (0、1)

1)源代码

       开发者编写的 C# 源代码,文件扩展名为 .cs。

2)公共语言规范(Common Language Specification,CLS) 编译

       源代码首先经过CLS编译。CLS 定义了一组规则和标准,确保不同.NET语言之间的互操作性。此步骤确保源代码遵循.NET的语言规则,以便不同语言(如 C#、VB.NET 等)能够相互调用。该过程的目的是实现跨语言。

3)通用中间语言(Microsoft Intermediate Language,CIL或MSIL)

       编译器将符合CLS的C#源代码转换为通用中间语言(CIL)。CIL 是一种与平台无关的低级语言,方便在不同操作系统上运行。

4)公共语言运行库(Common Language Runtime,CLR)编译

       CIL代码在运行时通过公共语言运行库(CLR)进行编译。CLR是.NET的执行环境,负责将 CIL 编译为特定平台的机器码。这一过程通常采用 JIT(即时编译,Just-In-Time Compilation)技术,在代码运行时将 CIL 动态转换为机器码。

5)机器码 (0、1)

       最终,经过CLR编译的代码转变为机器码(即二进制代码,使用0、1表示)。这些机器码是计算机能够直接执行的指令。

 

局部变量

       局部变量是定义在方法内部的变量,有以下特点:没有默认值,必须自行设定初始值,否则不能使用;局部变量随方法被调用时,存在栈中,随方法调用结束时从栈中清除

 

值类型与引用类型

       之前将值类型与引用类型在存储位置上是不同理解为:值类型在栈上分配内存,存储的是数据本身;引用类型:堆上分配内存,存储的是数据的引用(内存地址)。但实际并不全是如此,值类型可能在堆上分配内存。

 

       接下来从值类型和引用类型在栈中和堆中声明的情况来理解它们存储的区别:

1)在栈中声明

       值类型和引用类型变量在栈中声明。

//值类型a,b在栈中声明,数据均存储在栈中
int a = 1;
int b = a;
a = 2;
Debug.Log(b);//1

//引用类型arr1,arr2在栈中声明,数据存储在堆中,栈中储存该数据的引用
int[] arr1 = new int[] { 1 };
int[] arr2 = arr1;
arr1[0] = 2;
Debug.Log(arr2[0]);//2

ca0ddf0777de411d97d288ad2a223357.png

       因为方法在栈中声明,所以在方法中声明的变量都在栈中,此时(方法中的值类型和引用类型变量),因为值类型直接存储数据,所以值类型的数据存储在栈中,因为引用类型存储数据的引用,所以引用类型的数据存储在堆中,栈中存储数据的内存地址。

2)在堆中声明

       值类型(arr3的元素)和引用类型(arr4的元素)变量在堆中声明。

//index为值类型,声明在栈中,数据存储在栈中
int index = 0;

//arr3为引用类型,声明在栈中,数据存储在堆中,栈中存储数据的引用
//arr3的元素为值类型,声明在堆中,数据存储在堆中
int[] arr3 = new int[2];
arr3[0] = 1;
arr3[1] = 2;

string[] arr4= new string[1];
arr4[0] = "Hello, world!";
arr4[0] = "Hello, C#!";

a6ca8c8d9a494046b05863bb959275aa.png

 

总结

1)值类型:声明在栈中,数据存储在栈中;声明在堆中,数据存储在堆中。

2)引用类型:声明在栈中,数据存储在堆中,栈中储存该数据的引用;声明在对堆中,数据存储在堆的另一块空间。

 

拆装箱

       这里顺便来了解一下C#的拆装箱子操作:

 

1)装箱

       将值类型隐式转换为objec类型的过程。内部机制:在堆中开辟内存空间,将值类型的数据赋值到堆中,返回堆中新分配对象的地址。

2)拆箱

       将object类型转换为值类型的过程。内部机制:判断给定类型是否是装箱时的类型,返回已装箱实例中属于原值类型字段的地址。

int temp1 = 1;
//装箱操作,比较消耗性能
object o = a;
//拆箱操作,比较消耗性能
int temp2 = (int)o;

       

       好了,本次的分享到这里就结束啦,希望对你有所帮助~

猜你喜欢

转载自blog.csdn.net/grapefruit_lyy/article/details/143116151