1. 程式人生 > >淺談值型別和引用型別在堆和棧中的儲存一

淺談值型別和引用型別在堆和棧中的儲存一

首先,讓我們來簡單瞭解一下什麼是“棧”(stack),什麼是“堆”(heap)。“棧”其實就是一種後入先出(LIFO)的資料結構。在我們.NET Framework裡面,由CLR負責管理,我們程式設計師不用去擔心垃圾回收的問題;每一個執行緒都有自己的專屬的“棧”。“堆”的存放就要零散一些,並且由 Garbage Collector(GC)執行管理,我們關注的垃圾回收部分,就是在“堆”上的垃圾回收;其次就是整個程序共用一個“堆”。

我們先來記住兩條黃金法則:

1.引用型別總是被分配到“堆”上。

2.值型別總是分配到它宣告的地方:

   a.作為引用型別的成員變數分配到“堆”上

   b.作為方法的區域性變數時分配到“棧”上

要真正理解上面的兩條黃金法則,還需要了解一下,“棧”和“堆”是如何工作的。首先以下面的這個方法為例:

public int AddFive(int pValue){  int result;   result = pValue + 5;   return result;}

1.方法AddFive()被壓入“棧”

2.緊接著方法引數pValue被壓入“棧”

3.然後是需要為result變數分配空間,這時被分配到“棧”上。

4.最後返回結果

通過將棧指標指向 AddFive()方法曾使用的可用的記憶體地址,所有在“棧”上的該方法所使用記憶體都被清空,且程式將自動回到“棧“上最初的方法呼叫的位置。

下面我們來看看,值型別的變數分配到“堆”上到情況。

public class MyInt{    public int MyValue;}public MyInt AddFive(int pValue){    var result = new MyInt();    result.MyValue = pValue + 5;    return result;}

和前面一樣,執行緒開始執行函式,函式引數被壓入執行緒堆疊。

由於 MyInt 為引用型別,它被分配在“堆”上,並且由一個位於“棧”上的指標引用。

AddFive()函式執行完畢後,“棧”同樣會被清空。

最後,只剩下一個 MyInt 類被留在“堆”上(“棧”上再也沒有指向這個 MyInt 類的指標)!這個時候就需要GC機制來處理了。

好了,大家對“棧”和“堆”有一定的瞭解之後,下面讓我們來稍微深入一點,來看兩個例子。

public int ReturnValue(){    int x = 3;     int y = x;    y = 4;    return x;}

返回值大家其實已經知道了吧,還是3。這是為什麼呢?這是因為,值型別,使用的就是值本身,其實在做int y = x的時候,就把3做了一份“拷貝”賦值給了y,然後在y = 4的時候,操作的是這個“拷貝”,並不會操作原來的3。

接下來我們看另外一個例子,MyInt還是使用上面例子中使用過的MyInt類。

public int ReturnValue(){    var x = new MyInt();    x.MyValue = 3;     MyInt y;     y = x;    y.MyValue = 4;     return x.MyValue;}

在這個程式碼中,x.MyValue是否還是會是3呢?是否y.MyValue還是操作的3的“拷貝”呢?其實只要執行一下這個程式碼,就可以得出結果,x.MyValue還是被修改成了4。這是因為,當我們使用引用型別時,我們實際上是在使用指向這些物件的地址,而不是直接使用這些物件本身。

好了,暫時就介紹到這裡,如果有什麼疑問,或者有誤的地方,歡迎大家指點和交流