王风

关于值类型、引用类型以及“栈”跟“堆”的关系

值类型,声明一个值类型的时候,是在“栈”中开辟一个内存空间来存放对应的值,当值类型的值发生改变的时候,则直接修改该内存空间所保存的值。例:

int n1 = 5;
int n2 = n1;
Console.WriteLine(n1 + "  "+ n2);    // 5  5
n2 = 7;
Console.WriteLine(n1 + "  " + n2)    // 5  7

这里首先在“栈”中开辟一个内存空间用来保存 n1 的值 5,接着再在“栈”中开辟一个新的内存空间用来保存 n2 的值 5,所以显示出来的结果是 5 5。然后将 n2 在“栈”中对应的内存空间保存的值修改成 7,故显示出来的结果是 5 7。

引用类型,声明一个引用类型的时候,首先是在“堆”中开辟一个内存空间来存放对应的值,然后在“栈”中开辟一个内存空间用于保存在“堆”中开辟的内存空间的地址。当系统调用引用类型的时候,首先去“栈”中获取到地址,然后根据地址在“堆”中找到对应的内存空间来获取到对应值。像数组这样的引用类型

string[] a1 = new string[]{ "a" , "b" , "c" };
string[] a2 = a1;
for(int i = 0; i < a2.Length; i++)
{
    Console.Write(a2[i] + " ");    //a b c
}
a1[2] = "d";
Console.WriteLine();            //换行
for(int i = 0; i < a2.Length; i++)
{
    Console.Write(a2[i] + " ");    //a b d
}
Console.WriteLine(); 

这里首先是在“堆”中开辟一个内存空间(假设:0X55)用来保存数组a1的值,然后在“栈”中开辟一个内存空间(a1)用于保存地址 0X55。当将 a1 赋给 a2 时,是将地址赋给 a2,即在“栈”中开辟一个内存空间(a2)用于保存地址 0X55,所以输出 a2 的值是 a b c。当将 a1[2]修改成”d”的时候,修改的是“堆”中 0X55 内存空间保存的值,因为 a2 的地址和 a1 的地址一样,所以输出结果是 a b d。

而 string 是一个特殊的引用类型,先看下面代码:

string a = "123";
string b = a; 
Console.WriteLine(a+" "+b);  //123 123
string b = "456";
Console.WriteLine(a+" "+b);  //123 456

和数组类似的,这里首先在“堆”中开辟一个内存空间(假设:0X88)用来保存 a 的值 123,然后在“栈”中开辟一个内存空间(a)用于保存地址 0X88。

和数组不同的是,当将 a 赋给 b 的时候,首先是在“堆”中开辟一个新的内存空间(假设:0X101)用于保存值 123,然后在“栈”中开辟一个内存空间(b)用于保存地址 0X101,所以输出的结果是 123 123。

当修改 b 值时,并不是修改“堆”中 0X101 内存空间的值,而是在“堆”中重新开辟一个新的内存空间(假设:0X210)用于保存 b 修改后的值,然后将 b 在“栈”中对应的内存空间的所保存的地址修改成 0X210,所以输出的结果是 123 456。而“堆”中的 0X101 内存空间将在下次的垃圾回收中被回收利用。