C 函式引數傳遞 按值和引用

2022-06-01 10:06:08 字數 3380 閱讀 8357

c#中的資料型別.分值型別和引用型別兩大類.

值型別:直接儲存資料的值,儲存在記憶體中的stack(堆疊)中

引用型別:儲存對值的引用,實際上儲存的就是乙個記憶體的位址.引用型別的儲存分成兩塊,實際值儲存在託管堆(heap)中.實際值的記憶體位址儲存在stack中

當使用引用型別時先找到stack中的位址,再找到heap中的實際值.

也就是說儲存引用型別時要用到stack和heap,但使用引用型別時我們實際上只用到stack中的值,然後通過這個值間接的訪問heap中的值

c#預定義的簡單型別,像int,float,bool,char都是值型別,另外enum(列舉),struct(結構)也是值型別

string,陣列,自定義的class就都是引用型別了.其中的string是比較特殊的引用型別.c#給它增加個字元恆定的特性.

c#函式的引數如果不加ref,out這樣的修飾符顯式申明引數是通過引用傳遞外,預設都是值傳遞.

這裡要注意的乙個問題是,引數的型別是值型別還是引用型別和傳引數時用值傳遞還是引用傳遞是兩個不同的概念.

假如有void funtest(int array) 和void funtest(int a) 

這兩個函式.引數array是引用型別,a是值型別.但是他們傳遞時都是按值傳遞.

我們來舉個例子說明下

按值傳遞引數:

class program

public static void changeint(int num)

num = 123;

public static void changearray(int array)

array[0] = 10;

array = new int ;

static void main(string args)

int anum = 1;

int aarray = ;

changeint(anum);

changearray(aarray);

console.writeline("value of num: " + anum);

console.write("value of aarray: ");

foreach (int i in aarray)

console.write(i + " ");

結果是:value of anum : 1

value of aarray :10 2 3

可能看到結果會有點奇怪.我們一般認為值傳遞就是把值拷貝乙份,然後不管在函式中對傳入的引數做啥改變,引數之前的值不會受啥影響,所以anum沒有變成123,仍然是1

但是aarray[0]為啥卻變成10了呢?

前面我們有說到引用型別在記憶體中是儲存為兩個部分,乙個是stack中的記憶體位址,另乙個是heap中的實際值.用時我們只直接用stack中的值,我們假如stack中的值為0xabcdefgh 

,就說是aaraay指向它吧. 那麼我們按值傳遞時就是把這個stack的值拷貝成另乙份就假如是array指向它吧.跟拷貝anum的值1一樣.

但是我們操作記憶體位址這樣的值時不會像整數一樣直接操作它,而只會通過它去找heap中的實際值.

於是我們array[0] = 10.改變了實際上還是heap中陣列的值了. 但array = new int  

沒有對之前傳的aarray產生影響.這個操作的意義是在heap中重新開闢一塊記憶體,儲存著值6,7,8,9. 

這這塊記憶體的位址賦給array,於是它之前的值0xabcdefgh被改寫了.但aarray指的值stack值仍沒變,仍是0xabcdefgh

按引用傳遞引數

可以用out或ref顯式指定.它們大部分時候可以通用,只是有一點細小區別.

先用ref 來舉例吧,還用上面的例子,只是加個了關鍵字ref

class program

public static void changeint(ref int num)

num = 123;

public static void changearray(ref int array)

array[0] = 10;

array = new int ;

static void main(string args)

int anum = 1;

int aarray = ;

changeint(ref anum);

changearray(ref aarray);

console.writeline("value of num: " + anum);

console.write("value of aarray: ");

foreach (int i in aarray)

console.write(i + " ");

結果是:value of anum : 123

value of aarray :6 7 8 9

跟按值傳遞的結果完全不同吧

num = 123我們是容易理解.我們再來說下aarray的值為啥變了吧

按引用傳遞時aarray指向的stack中的值不會複製乙份,而是直接傳過去.這樣array[0]= 10這樣賦值時也同樣改變了heap中 1 2 3 

的值,變為10 2 3,如果

沒有array = new int  

這個語句,則它的結果跟上面按值傳遞是完全一樣的.但有個這句話後就不一樣,我們知道上面說了它的含義,在heap中開闢一塊新記憶體

值是6 7 8 9,而aarray指向的stack的值被改寫了,改為指向儲存6 7 8 9的記憶體位址了.那含有10 2 

3的那一塊記憶體其實還繼續存在,只是沒有誰引用到它了.到時垃圾**器會把它**的.

補充:說下out 和ref的細小區別

ref 傳進來的引數必須要先賦值.

像上面 的例子中如果這樣寫

int num;

changeint(ref int num);

就會出錯,必須先給num給個值1.

而且out傳進來的引數可以不先賦值.

out num;

changeint(out int num);是對的

另外還有個區別就是如果用out的時候changeint函式中必須有某個地方給num賦值了,而用ref不一定需要在函式中給num賦值

其實這樣做的目的很好理解.c#為了確保在任何情況下num必須有個值,不能為空.

因為用ref,在呼叫函式前必須保證引數有值,所以在函式中就不必要求它一定再賦值

而用out由於在呼叫函式前不用保證引數必須有值,所以在函式中必須保證給它個值

changeint(ref int num)和changeint(out int num)雖然不一樣,但是不同共存,不能當作兩個不同的函式

而changeint(int num)和上面 的兩個函式是完全不一樣的,可以放到一起共存

這樣的話呼叫的時候ref ,out這樣的關鍵字不能省的.必須匹配

Python函式引數 按值傳遞和按引用傳遞

首先簡單說明一下,python中按值傳遞與按引用傳遞的區別 按值引數傳遞是指乙個變數的值來取代乙個函式引數的做法。如果這個值在函式 組中改變,對於呼叫這個函式的 來說,其中相應的變數值並不會受到任何影響,可以把這個引數認為是原變數值的乙個副本。按引用引數傳遞是指對於呼叫這個函式的 會維護這個 中變數...

Python按值傳遞引數和按引用傳遞引數

python按值傳遞引數和按引用傳遞引數 按值傳遞引數 使用乙個變數的值 數字,字串 放到實參的位置上 注 傳遞過去的是變數的副本,無論副本在函式中怎麼變,變數的值都不變 傳遞常量 傳遞常量 定義乙個函式,輸出 歡迎,接收到的引數 name defhuanying name print 歡迎 nam...

C 值傳遞和按引用傳遞

知識點 值型別和引用型別 為值型別,據 對於引用型別來說,棧中儲存的是堆中物件的位址 值傳遞和引用傳遞 對於值傳遞,傳遞的是棧中儲存的資料 對於引用傳遞。傳遞的是棧本身的位址 先看一下值傳遞 傳遞值型別和引用型別 class program 值型別的值傳遞 static void d1 int m ...