網上很多文章寫C#的值類型和引用類型等相關知識,這次我想用我的C語言知識去闡述這個內容,去理解類似C#這種面向對象的語言中值類型和引用類型。
說到引用類型首先想起的就是指針,所以這也是我想從C語言方向闡述這個內容的原因。Ref就是模仿指針功能,進行傳遞的。
一、基本的棧和堆
我們常說的堆棧,其實是有兩層含義
A、棧和堆是兩種數據結構
棧:一種先進后出的數據結構。
堆:堆可以被看成是一棵樹。
B、內存中的位置
棧:棧使用的是一級緩存, 他們通常都是被調用時處於存儲空間中,調用完畢立即釋放;
堆:堆是存放在二級緩存中,生命周期由虛擬機的垃圾回收算法來決定(並不是一旦成為孤兒對象就能被回收)。所以調用這些對象的速度要相對來得低一些。
二、操作語言中的棧和堆
棧:我們平時調用函數,main函數調用功能函數,功能函數再調用下屬的函數,其實就是一個壓棧的過程。Main函數始終是最后出棧的,而最后調用的函數和他的參數,會最先出棧,這也是我們程序的執行順序。
堆:堆一般由程序員分配釋放,若程序員不釋放,目前很多語言都有自動的垃圾收集器,這樣也方便非常方便我們編程。我們很多的實例化之后的類和泛型,其實都是存儲在堆空間中的。
C#默認是值傳遞的,無論值類型還是引用類型。
但是C#中的引用類型變量,其實就是存儲在棧空間中,指向堆內存某個位置的地址變量。而這塊堆內存中存儲的數據的類型就是這個引用類型變量的數據類型。
而我們的函數在傳遞引用類型參數值的過程中或者進行引用類型復制過程中,默認是新建一個棧內存空間,利用新的空間復制原有引用類型變量中存儲的值(注意:這個值是指向堆內存地址的位置編碼)。也就是說,棧內存中,出現了2個新的變量,存儲的值相同,都是堆內存地址編碼。具體示例如下圖。
三、程序實例
最后附上一個C#程序,通過這個程序能更好的理解C#的值類型傳遞和引用類型傳遞。
///值類型參數 按值傳遞 和按引用傳遞的區別
class Program { static void Main(string[] args) { int a = 0; Add(a); Console.WriteLine(a); Add(ref a); Console.WriteLine(a); Console.Read(); } static void Add(int i) { i = i + 10; Console.WriteLine(i); } static void Add(ref int i) { i = i + 10; Console.WriteLine(i); } } 結果:10,0,10,10; ///引用類型參數 按值傳遞和按引用傳遞的區別
class Program { static void Main(string[] args) { ArgsByRef a = new ArgsByRef(); Add(a); Console.WriteLine(a.i); Add(ref a); Console.WriteLine(a.i); Console.Read(); } static void Add(ArgsByRef a) { a.i = 20; Console.WriteLine(a.i); } static void Add(ref ArgsByRef a) { a.i = 30; Console.WriteLine(a.i); } } class ArgsByRef { public int i = 10; } 結果 :20,20,30,30; ///字符串參數的按值與按引用傳遞 與 值類型一致 (string 是引用類型)
class Program { static void Main(string[] args) { string a = "Old String"; Add(a); Console.WriteLine(a); Add(ref a); Console.WriteLine(a); Console.Read(); } static void Add(string a) { a = "new String"; Console.WriteLine(a); } static void Add(ref string a) { a = "new String"; Console.WriteLine(a); } } 結果:new String, Old String,new String,new String;