C#:值类型变量和引用类型变量在内存中的存储方式
计算机存储和读取数据的最小单位是字节、再每个字节编上唯一的编号后大概便是下面的样子。
我们常说的内存地址,就是指数据在内存中的内存编号。按照编号查找某个数据在内存单元中的位置,称为寻址。
对于操作系统,他保留了一段内存区域以供操作系统来使用,其它程序不允许使用这个内存。----在上图中用 黄色标记。
对于程序来说,他们使用内存就是剩下的区域。
值类型变量:以变量名所对应的内存地址为起点,以其数据类型所要求的存储空间为长度的一块内存区域。
值类型变量在内存中是如何存储的:
例子1:byte a; a=100;为例。
分析:
1、看到byte时,我们知道变量a占据一个字节的内存空间。
2、计算机去寻找内存中,哪里有空闲的存储空间,则从此处分配内存空间。
3、a=100,100用二进制补码表示时:01100 0100,存储到分配给a的空余空间中。
所以,最终 10000007 就是变量a的内存地址。
例子2:以 sbyte b;b=-100;为例:
1、sbyte 是有符号的整型,最高位表示符号位,取值范围从-128~+127;
2、我们知道计算机中存储的数是以二进制补码的形式存放的,-100的补码:10011100
3、计算机会为变量b寻找到一个空余内存,并分配一字节的空间,并将10011100填充到这块空间中
所以,变量b在内存中的存储形式便是上图,b变量的内存地址是 10000010
例子3:以ushort c=1000;为例:
1、ushort 两个占据两个字节的存储空间,表示无符号整数
2、1000的二进制形式位:0000 0011 1110 1000
3、对于超过一个字节的变量,数据在内存中存储的规则:高高低低原则--即:高位放在内存地址编号大的位置、低位放在内存地址编号小的位置。
所以,ushort类型变量c的内存地址是 1000 0016。
例子4:对于值类型数据,他们可以调用Convert.Tostring()方法,来快速求出二进制表示形式,解决了我们求类似-1000的补码比较慢的问题。
short c = -1000; string s = Convert.ToString(c, 2); Console.WriteLine(s);
运行结果:1111 1100 0001 1000
引用类型变量:引用类型变量里面存储的是实例的内存地址。
下面来看一下引用类型变量和引用类型实例在内存中的对应关系。
比如:在Main()方法中有下面一段代码:Student student;student=new Student();已知Student类型中有两个成员 int类型ID、short类型score;
1、当程序解析到 Student student时,判断到student是引用类型的变量,那么分配内存的方式和值类型完全不同。
2、值类型变量是按照,变量的实际大小分配空间的,而引用类型变量直接会被分配四个字节的空间,并且初始值都设置为0(也就是null)。
3、当解析到student=new Student()时,分两步操作,
- 第一步:相应new Student(),在堆内存中根据成员类型和个数分配相应大小的空间
- 第二部:将对象在堆内存中的其实内存地址的值转换成二进制值,赋值给引用类型变量
经计算,30000009的二进制值是:00000001110010011100001110001001 按照高高低低原则,给student变量所占的内存区域赋值后。
4、最终student变量存储的便是Student类型实例的内存地址,我们可以说,引用类型变量通过实例的地址指向了堆中了一个实例。
总结:
1、局部变量 (比如stundent、a、b、c等,都是在Mian方法中的) 在Stack上面分配内存
2、实例变量 (比如Student类型中的 ID、Score字段等) 在Heap上面分配内存
以上是对引用类型变量和值类型变量在内存中的存储方式的总结,记录下来,以便以后查阅。