你真的了解C 中的值和引用嗎?(下)

2022-01-30 23:16:22 字數 2836 閱讀 2046

今天要討論的話題是引數傳遞,這不是實現細節。

c#中的引數共分為4種:

本文主要討論引數按值傳遞和按引用傳遞的區別,以及值型別和引用型別在按值傳遞和按引用傳遞時的表現。我們均以向方法傳遞引數為例。

c#的引數在預設情況下都是按值傳遞的。也就是說,當向方法傳遞引數的時候,會建立乙個新的儲存位置,然後將引數的值複製乙份放到該儲存位置中。相當於宣告了乙個區域性變數(實參),然後用傳入的引數的值初始化這個變數。如果在方法內改變實參的值,將不會影響到方法呼叫的上下文。

上篇文章提到過,值型別表示式的值是資料本身,引用型別表示式的值是到物件的引用。所以,按值傳遞的時候,對值型別的引數來說,複製的是該值型別引數所儲存的資料;對引用型別的引數來說,複製的值是到具體物件的引用。(注意,這和直接用變數對另乙個變數賦值是一樣的。)

注意如剛才所說,值型別按值傳遞時,將複製該值型別本身所代表的資料。如下面的**片段:

// #code1

int i = 5;

n(i);

console.writeline(i);

...void n(int j)

雖然在n內部,j被設定為10,但實際上在方法呼叫的上下文中,i的值仍然是5,改變的是i的乙個副本j。如下圖所示:

對引用型別的引數來說,複製的值是到具體物件的引用。如下面的**片段:

// #code2

stringbuilder sb1 = new stringbuilder("hello");

m(sb1);

console.writeline(sb1);

...void m(stringbuilder sb2)

雖然在m內部,sb2被設定為null,但實際的輸出結果仍然是「hello」。如下圖所示:

注意,我們說的是在方法內部改變實參的,不會對外部呼叫上下文產生影響。對於引用型別的實參,如果在方法內部更改所引用的物件的資料,實參和外部變數仍然引用的是同樣的物件,因此都會受到影響。例如下面的**:

// #code3

stringbuilder sb1 = new stringbuilder("hello");

m(sb1);

console.writeline(sb1);

...void m(stringbuilder sb2)

輸出結果將為「hello world」。如下圖所示:

按引用傳遞不會涉及隱式複製。它所傳遞的,不是在呼叫方法時傳遞給方法的變數的值,而是變數本身。它不會建立新的儲存位置,而是使用與變數相同的儲存位置,因此,在呼叫方法上下文中傳遞給方法的變數與方法內部使用的引數,實際上是同乙個。

在按引用傳遞引數時,在方法的宣告和呼叫的地方都必須顯式使用ref修飾符,這是為了讓你清楚你正在進行的是與預設傳遞方式不同的按引用傳遞。

按照引用傳遞的定義,我們實際上是把變數本身傳遞給了方法,在方法內和呼叫方法的地方,使用的實際上是同乙個變數。因此對於值型別來說,在方法內部對引數所做的任何改動,也都會反映到方法外部。#code1改成按引用傳遞後,輸出結果將為10。

// #code4

int i = 5;

n(ref i);

console.writeline(i);

...void n(ref int j)

引數的傳遞過程如下圖所示:

如圖所示:

我們通常提到c#中的值和引用,大多數情況可能都是指值型別和引用型別,但實際上值和引用有著更加豐富的含義。我這兩篇文章試圖把這些概念總結出來,講解了值型別和引用型別的值是什麼,以及它們在按值傳遞和按引數傳遞時有什麼相同和不同之處。更重要的是,關於值型別和引用型別,我們平時在認識上存在很多誤區,把相關的實現細節作為知識點記憶了下來。其實這些只是其然,不是所以然。而所以然,我們也沒必要去深究。

很多面試官在考察面試者時,也會不自覺得去問一些實現細節,這完全沒有必要。(當然,很可能面試官也陷入了這樣的誤區。)以後我們應該牢記的就是,當提到引用型別和值型別區別的時候,如果有人扯到儲存位置這個話題上來(90%的人都大概會這樣吧),你應該把他們引導到正確的方向上來。

你真的了解C 中的值和引用嗎?(上)

術語解釋 注意,上面我說的都是值型別表示式和引用型別表示式,包括區域性變數和成員 如欄位 屬性 索引器 等。現在,我們來考慮以下問題 對於上面這些問題,您的答案是什麼呢?誤區 值型別到底儲存在哪?在談到值型別和引用型別的區別時,很多初學者常說值型別分配在方法的呼叫棧 或執行緒棧 上,引用型別分配在託...

C 的那些事 你真的了解引用嗎

說到引用,一般c 的教材中都是這麼定義的 1,引用就是乙個物件的別名。2,引用不是值不佔記憶體空間。3,引用必須在定義時賦值,將變數與引用繫結。那你有沒有想過,上面的定義正確嗎?編譯器是如何解釋引用的?這裡先給出引用的本質定義,後面我們再進一步論證。1,引用實際是通過指標實現的。2,引用是乙個常量指...

你真的了解EOF和feof嗎?

判斷檔案結束有兩種方法 eof和feof 檢視stdio.h可以看到如下定義 define eof 1 define ioeof 0x0010 define feof stream stream flag ioeof 由此可以看出,這兩種方式的原理是不同的。有人說eof只能用於文字檔案,其實不然,還...