C 引用型別作為函式引數時

2021-06-03 01:29:21 字數 4182 閱讀 3403

在**本文的主題之前,先來介紹下c#中的值型別和引用型別

眾所周知c#中有值型別和引用型別,值型別有基礎資料型別(諸如int,double,bool等)、結構體、列舉,引用型別有介面、類、委託。

值型別全部在作業系統的棧空間中申請,而引用型別則在作業系統的堆空間中建立物件,然後在棧空間中申請乙個指標指向這個物件的位址。

因此c#的引用型別其實就如同c++的指標型別。

下面我再來看看函式傳參的問題。

早在c時代就有函式引數傳值和傳位址的概念,請記住在c#中函式引數預設都是傳值。

所以不管是值型別還是引用型別在作為引數傳進函式時,其實都是傳的值,只不過引用型別傳的是物件在堆中的的位址罷了。

而且從上面的定義可以看出c#中引用型別的變數用c++來說就相當於是該引用型別的指標,比如有類(引用型別)refclass:

refclass rc就相當於是c++上的refclass *rc

在c#中使用rc.intvalue++;時,相當於c++的rc->intvalue++;

因為引用型別在函式傳參時是傳位址的,所以我腦袋裡就形成了一種慣性思維,認為只要傳進函式的是引用型別,那麼在函式中做的任何更改都會反映到實參上。但是我發現並不完全是這樣,下面給出個例子(注釋內容為對應等效的c++**):

using

system;

using

system.collections.generic;

using

system.linq;

using

system.text;

namespace

refwarn

}class

program

static

void

addvalue(

refrefclass prc)

//refclass **prc,prc指向傳進來的rc的位址

static

void

changeref(refclass prc)

//refclass *prc,prc和傳進來的rc指向同乙個refclass物件的位址;//

prc=new refclass() ;請注意new關鍵字在c++中建立的是指向物件的指標不是物件

}static

void

changeref(

refrefclass prc)

//refclass **prc,prc指向傳進來的rc的位址;//

*prc=new refclass() ;請注意new關鍵字在c++中建立的是指向物件的指標不是物件

}static

void

main(

string

args);//

refclass *rc=new refclass() ;請注意new關鍵字在c++中建立的是指向物件的指標不是物件

addvalue(rc);

//rc,傳遞指向refclass物件的指標

console.writeline(

"呼叫addvalue(rc)後intvalue為:"+

rc.intvalue);

rc.intvalue =1

;addvalue(

refrc);

//&rc,傳遞指向refclass物件指標的指標

console.writeline(

"呼叫addvalue(ref rc)後intvalue為:"+

rc.intvalue);

rc.intvalue =1

;changeref(rc);

//rc,傳遞指向refclass物件的指標

console.writeline(

"呼叫changeref(rc)後intvalue為:"+

rc.intvalue);

rc.intvalue =1

;changeref(

refrc);

//&rc,傳遞指向refclass物件指標的指標

console.writeline(

"呼叫changeref(ref rc)後intvalue為:"+

rc.intvalue);}}

} 複製**

你會發現在main函式中呼叫changeref(rc)後,rc並沒有發生改變,其屬性intvalue的值還是1。

這是為什麼?我們先來看看static void changeref(refclass prc)函式的結構,看看裡面都做了什麼

static void changeref(refclass prc)//refclass *prc,prc和傳進來的rc指向同乙個refclass物件的位址

;//prc=new refclass() ;

}可以看到函式裡就是對refclass 型別的形參引用變數prc重新賦了值。但是最後我們看到這個賦值並沒有反應到實參引用變數rc上。原因其實很簡單就像本文開始所說的一樣,由於實參變數pc和形參變數rpc都是引用型別的變數,那麼它們實際上是在作業系統棧空間上的兩個指標,只不過指向的是作業系統堆空間上的同乙個refclass 物件。在函式changeref中對引用變數rpc重新賦值,相當於是將棧中的rpc指標重新指向了堆中的另乙個refclass 物件。形參變數rpc指向的位址改變後,並不會對實參變數pc的指向發生改變,所以pc還是指向函式changeref(refclass prc)呼叫前的那個refclass 物件。

但是也許你又會問為什麼addvalue(rc)執行後,函式對rc做了更改呢?我們來看看addvalue(refclass prc)函式

static void addvalue(refclass prc)//refclass *prc,prc和傳進來的rc指向同乙個refclass物件的位址

請注意函式addvalue並不是更改了實參引用變數rc,它更改的是rc指向的refclass 物件的屬性,是因為實參變數pc和形參變數rpc都指向同乙個refclass 物件的原理,所以在addvalue裡面rpc更改了它所指向refclass 物件的屬性,也就等於更改了pc指向refclass 物件的屬性。所以才在執行addvalue(rc)後給人一種好像rpc和pc是同乙個變數,更改了rpc就等於更改了pc的錯覺。但是請記住這是絕對錯誤的,rpc和pc是兩個完全不同的引用變數,只不過指向的是記憶體中的同乙個refclass 物件。

最後我們來**下有沒有辦法使函式在傳遞引用型別的引數時,讓形參完全等於實參呢?能否做到不管對形參是重新賦值還是做更改,都反映到實參上?

答案是肯定就是使用ref關鍵字

這個關鍵字用在值型別上的時候,就相當於c++的指標型別,比如:

ref int param

就相當於c++的

int *param

且該指標指向的就是其對應的實參變數

所以在c#中使用宣告為ref的int形參變數param.tostring()時候,相當於c++上使用int指標*param.tostring()

所以在使用宣告為ref的int形參param時,就相當於是c++上的*param,其操作的就是param指向的那個int變數,即實參。

而當這個關鍵字用在引用型別前面的時候,就相當於是指向引用型別變數的位址,而前面說過c#引用型別的變數就相當於是c++的指標,那麼指向引用型別變數的位址也相當於就是指向指標的指標。

因為前面說了refclass rc相當於c++的refclass *rc

那麼ref refclass rc相當於c++的refclass **rc

在c#中使用宣告為ref的refclass變數rc.tostring()時,當於c++上上使用refclass指標的指標*rc->tostring()

所以在使用宣告為ref的refclass型別形參rc時,就相當於是c++上的*rc(注意*rc還是指標,因為rc是指向指標的指標),其操作的是形參rc指向的那個refclass型別的引用變數(即rc指向的是實參變數的位址,而不實參變數指向堆空間中物件的位址),即實參。

而實參前面的ref相當於是c++的&符號即取該變數的位址。

所以在函式形參前加上ref那麼形參變數指向的就是實參變數的位址,只不過如果實參型別是值型別,那麼形參變數指向的就是該實參變數在作業系統棧中的位址。如果實參是引用型別,那麼形參變數指向的也是實參變數在作業系統棧中的位址,只不過該實參變數又指向物件在作業系統堆中的位址。所以無論是引用型別還是值型別,只要在其作為形參時在前面加上ref,那麼形參變數都是指向實參變數的指標,則操作形參變數就等於是在操作實參變數。

最後一定要清楚在引用型別做函式形參時,加上ref和不加ref的不同。

還是拿refclass rc來舉例:

C 引用型別作為函式引數時

在 本文的主題之前,先來介紹下c 中的值型別和引用型別 眾所周知c 中有值型別和引用型別,值型別有基礎資料型別 諸如int,double,bool等 結構體 列舉,引用型別有介面 類 委託。值型別全部在作業系統的棧空間中申請,而引用型別則在作業系統的堆空間中建立物件,然後在棧空間中申請乙個指標指向這...

C 引用型別作為函式引數時

在 本文的主題之前,先來介紹下c 中的值型別和引用型別 眾所周知c 中有值型別和引用型別,值型別有基礎資料型別 諸如int,double,bool等 結構體 列舉,引用型別有介面 類 委託。值型別全部在作業系統的棧空間中申請,而引用型別則在作業系統的堆空間中建立物件,然後在棧空間中申請乙個指標指向這...

C 引用作為函式引數

有了變數名,為什麼還需要乙個別名呢?c 之所以增加引用型別,主要是把它作為函式引數,以擴充函式傳遞資料的功能。到目前為止我們介紹過函式引數傳遞的兩種情況。1 將變數名作為實參和形參 這時傳給形參的是變數的值,傳遞是單向的。如果在執行函式期間形參的值發生變化,並不傳回給實參。因為在呼叫函式時,形參和實...