C 引用引數

2022-08-28 08:57:12 字數 4966 閱讀 1202

最近經常和同事討論引用引數的問題,為了搞清楚,查了些資料,其中clr via c#中講的比較清楚,整理了下

----摘自(clr via c#)

在預設情況下,clr假設所有的方法引數都是按值傳遞的。當引數為引用型別的物件時,引數的傳遞時通過傳遞指向物件的引用來完成的(引用本身是按值傳遞的)。這意味著方法可以改變引用物件,並且呼叫**可以看到這種改變的結果。

對於乙個方法,我們必須知道它的每個引數是引用型別引數,還是值型別的引數,因為我們編寫的操作引數的**會因此有很大的差別。

除了按值傳遞引數外,clr還允許我們按引用的方式來才傳遞引數。在c#中,我們可以用out和ref關鍵字來做到這一點。這兩個關鍵字告訴c#編譯器要產生額外的元資料來表示指定引數是按引用的方式來傳遞的:編譯器將使用該資訊來產生傳遞引數位址(而不是引數本身的值)的**。

關鍵字out和ref的不同之處在於哪個方法負責初始化引數。如果乙個方法的引數被標識為out,那麼呼叫**在呼叫該方法之前可以不初始化該引數,並且被呼叫方法不能直接讀取引數的值,它必須在返回之前為該引數賦值。如果乙個方法的引數被標識為ref,那麼呼叫**在呼叫該方法之前必須首先初始化該引數。被呼叫方法則可以任意選擇讀取該引數、或者為該引數賦值。

引用型別引數和值型別引數在使用out和ref關鍵字時的行為有很大的區別。下面我們先來看一看在值型別引數上使用out關鍵字時的行為:12

3456

78910

1112

1314

15classprogram

staticvoidsetval(outint32 v)

}

我們再來看一看在值型別引數上使用ref關鍵字時的行為:12

3456

78910

1112

1314

1516

1718

classprogram

staticvoidaddval(refint32 v)

}

從il或者clr的角度來看,out和ref關鍵字的行為實際上是一樣的:它們都會導致指向例項的指標被傳遞給方法。兩者的不同之處在於編譯器會根據它們選擇不同的機制來確保我們的**是正確的,例如,下面的**檢視向乙個需要ref引數的方法傳遞乙個未經初始化的值,從而導致編譯錯誤:12

3456

78910

1112

1314

1516

17classprogram

staticvoidaddval(refint32 v)

}

另外,clr允許我們根據out和ref引數來過載方法。例如,下面的**就是合法的:12

3456

789classpoint

staticvoidadd(refpoint p)

}

但是僅通過區分out和ref來過載方法又是不合法的,因為它們經jit編譯後的**是相同。所以我們不能在上面的point型別中再定義下面的方法:

1staticvoidadd(outpoint p)

在值型別引數上使用out和ref關鍵字與用傳值的方式來傳遞引用型別的引數在某種程度上具有相同的行為,對於前一種情況,out和ref關鍵字允許被呼叫方法直接操作乙個值型別例項。呼叫**必須為該例項分配記憶體,而被呼叫方法操作該記憶體。對於後一種情況,呼叫**負責為引用型別物件分配記憶體,而被呼叫方法通過傳入的引用來操作物件。基於這種行為,只有當乙個方法要「返回」乙個它已知的物件引用時,在引用型別引數上使用out和ref關鍵字才有意義。看下面的**:12

3456

78910

1112

1314

1516

1718

1920

2122

2324

2526

2728

2930

3132

33class

}

staticvoidstartprocessingfiles(outfilestream fs)

staticvoidcontinueprocessingfiles(reffilestream fs)

}

如我們所見,這段**中最大的不同在於有著out或者ref修飾的引用型別引數的方法建立乙個物件後,指向新物件的指標會被返回給呼叫**。另外注意continueprocessingfiles方法在返回新物件之前可以操作傳入的物件,這是因為其引數被標識為ref。

下面的**是上述**的乙個簡化版本:12

3456

78910

1112

1314

1516

1718

1920

2122

2324

25class

}

staticvoidprocessingfiles(reffilestream fs)

}

下面的例子演示了怎樣使用ref關鍵字來交換兩個引用型別:12

3456

staticpublicvoidswap(refobjecta,refobjectb)

12

3456

78910

1112

1314

15staticpublicvoidsomemethod()

可以看到,修正後的somemethod會通過編譯,並且會按我們所期望的行為執行,c#要求以引用方式傳遞的引數必須和方法期望的引數完全匹配的目的是為了確保型別安全。下面的**展示了如果型別不匹配可能導致型別安全漏洞(不會通過編譯)。12

3456

78910

1112

1314

1516

1718

1920

2122

23classsometype

class

staticvoidgetanobject(outobjecto)

}

在這段**中,main期望getanobject返回乙個sometype物件。但是,因為getanobject得簽名表示的是乙個指向object的引用,所以getanobject可以將o初始化為乙個任何型別的物件。當getanobject返回到main中時,st將指向乙個string,這顯然不是乙個sometype物件,對console.writeline的呼叫自然會失敗。幸運的是,c#編譯器不會編譯上面的**,因為st是乙個指向sometype的引用,而getanobject要求的是指向object的引用

C 引用引數

最近經常和同事討論引用引數的問題,為了搞清楚,查了些資料,其中clr via c 中講的比較清楚,整理了下 摘自 clr via c 在預設情況下,clr假設所有的方法引數都是按值傳遞的。當引數為引用型別的物件時,引數的傳遞時通過傳遞指向物件的引用來完成的 引用本身是按值傳遞的 這意味著方法可以改變...

C 風格 引用引數

所有按引用傳遞的引數必須加上 const.定義 在 c 語言中,如果函式需要修改變數的值,引數必須為指標,如 int foo int pval 在 c 中,函式還可以宣告引用引數 int foo int val 優點 定義引用引數防止出現 pval 這樣醜陋的 像拷貝建構函式這樣的應用也是必需的.而...

c 引數傳遞之引用引數

每乙個想要做到模組化程式設計的碼農都不可避免的乙個問題是引數傳遞 如果您在這裡覺得有疑問,誒,我不想做模組化呢,以筆者經驗這個你大可不必揪心,因為你的老師或者老闆會讓你做到的 以c語言為例,主調函式傳參給被調函式的情況中,87 的情況都是要改變引數的值。這時候有兩種方法做到這個,第一傳指標,一般情況...