從C 中的引用型別到String

2021-07-06 01:47:55 字數 2753 閱讀 6376

最近面試被問到乙個問題,在c#裡面string是值型別還是引用型別,當時想都沒想就說是引用型別。後來面試官又接著問為啥,我就給愣住了,隨口說了個可以為null就不會了。下來仔細想了想這個問題。

首先,要說明string為什麼是引用型別,先來考慮下什麼是引用型別,什麼是值型別,以及他們的區別是什麼。在網上隨便搜搜基本上的解釋都是,值型別儲存在棧上,引用型別儲存在堆上。這句話基本上說明不了什麼。

在仔細想想,為什麼當初設計c#語言的時候要分為值型別和引用型別呢。全都搞成一樣不是更簡單麼。可能就是因為在c#裡面取消了c++裡的指標,而通過引用型別可以實現之前指標可以實現的功能。(不太懂指標,所以就不多說了)我能想到的區別就是在函式傳參的時候,比如我有乙個函式

void main(int b)

我在呼叫他時傳了乙個引數a ,  可以這樣寫  

int a = 100;

main(a);

結果就是執行完這個函式後,a的值不會改變,那麼其實我是把a的值(比如說100)給了函式,然後函式裡面可以用這個值進行計算之類的,不管函式裡面怎麼變都不會改變a本身,這也就是函式傳參時的形參和實參的區別,不管引數是什麼型別的(值型別或者引用型別),在給乙個函式傳參的時候都是把實參(比如上面的a)的值取出來賦給形參(b),形參其實就是乙個區域性變數,在函式被呼叫時建立,在函式執行完後銷毀。但如果有一天我寫了乙個程式

int a = 100;

a = 10;

console.write(a);

我想把a=10;這句寫在乙個函式裡,方便以後重複呼叫(是不是有點多此一舉....),如果按照上面的寫法肯定是不行的,因為函式裡的形參的改變是不會影響外面的a。在c++裡面好像可以用取位址符(&)傳參實現。但是標準的c#裡面沒有這個東西了,怎麼辦呢。這個時候就要用到引用型別了。可以這麼寫把我想要改變的變數封裝到乙個類裡面。

class a

static void main(string args)

static void main(a b)

所有的類都是引用型別的。在傳參的時候雖然也是通過複製值傳遞,但是由於引用型別裡面存的其實是乙個位址,所以傳遞值的時候其實也是傳遞的位址值,但在實際操作的時候會根據位址找的具體的值(a1,a2,a3)的位置然後操作。在記憶體裡過程如圖:

上面**裡首先例項化乙個物件a,a的具體值(a1,a2,a3)會存在堆裡(假設位址是0001),然後在棧裡的乙個位置存下這個位址0001,a就是指向棧裡的這個位置。當把a當引數傳給函式時,函式會例項化乙個形參b(在棧裡,並沒有在堆裡分配新的空間)然後將a的值(0001)複製給b。由於傳的值是乙個位址0001,所以在實際操作b的時候就是操作堆裡0001位置的值。也就相當於操作了a的值。

其實在函式傳參的時候還有引用傳遞的情況,運用關鍵字ref和out。

但這些都是書上說的理論,感覺總有點不真實,於是寫一段**驗證下:

class a

static a a = new a();

static int aa ;

static void main(string args)

static void main(a b,int bb)

執行的結果是:

object.referenceequals方法用來判斷兩個物件是不是引用同乙個東西。

可以看到值型別a在函式傳參以後指向的還是以前的物件,而值型別int則不是。

然後再試試string型別

static string  str;

static void main(string args)

static void main(string a)

結果果然還是true,看來string確定是引用型別無疑了。

關於string型別還有一些很神奇的,比如string型別的值是readonly的,如果要改變乙個string物件的值,那麼其實是新建了乙個string物件。

string str = "aaa";

string str1 = str;

console.writeline(object.referenceequals(str1, str));

str1 = "aaaa";

console.writeline(object.referenceequals(str1, str));

console.readline();

結果是true  和false   所以在頻繁改變字串值的時候還是用stringbuilder比較好。

還有一點比較神奇的是,clr會自動給string型別建立一張記錄表,每當新初始化乙個string物件時,先查記錄表,如果已經存在相同的字串,則直接指向它的位置,這樣就不用再次分配空間了。例如

string str = "aaa";

string str1 = "aaa";

console.writeline(object.referenceequals(str1, str));

這樣的**結果也是true,str和str1其實是指向堆裡面的同乙個位置。

從c到c 引用與常引用

下面的寫法定義了乙個引用,並將其初始化為引用某個變數。型別名 引用名 某變數名 int n 4 int r n r引用了 n,r的型別是 int 某個變數的引用,等價於這個變數,相當於該變數的乙個別名。例 int n 4 int r n r 4 cout r 輸出 4 cout n 輸出 4 n 5...

C 引用型別之特例string

在c 程式設計的時候經常會使用字串 string 型別,它也是引用型別,但是處處都不作為引用的用法來使用,實屬特例,下來我一一羅列出來,供自己記憶方便 1 字串的直接賦值 本身字串就是引用型別,應該使用 new 物件方法乙個例項,但是微軟為了方便大家,可以直接定義字串變數 並且賦值操作,例如 str...

String是值型別還是引用型別(C )

msdn 中明確指出 string 是引用型別而不是值型別,但 string 表面上用起來卻像是值型別,這又是什麼原因呢?首先從下面這個例子入手 值型別 int a 1 int b a a 2 console.writeline a is b is a,b 引用型別 string str1 ab s...