何為傳值call by value、傳址call by address、傳參考call by reference?
『也可以叫做pass by value、pass by address、pass by reference』
傳址call by address傳說中是台灣人發明的講法,
其實傳址它本質上也是call by value,或者是call by value of pointer,至於為什麼晚點再說明
傳值call by value:
swap意思是交換,所以這段程式碼很明顯的就是想交換主程式a跟b的值,
如果你連上面的程式碼都搞不清楚,可能得先建議你去網路或者看書學一些基本的概念。
在這段程式碼中,我們在主程式宣告了a跟b的值,見下圖:
main的a | main的b | ||||
儲存值 | 5 | 10 | …… | …… | …… |
記憶體位址 | 0×04 | 0×08 | …… | …… | …… |
PS:記憶體的位址應該會是像0x28ff44這種樣子,可是為了方便所以我把它縮短成如上所示
當程式執行第一行的宣告後,系統會立即分配記憶體空間給變數使用,且將儲存的值存到變數裡
接著當下一行我們呼叫了副程式swap(a,b),意思就是要把a跟b的引數給swap副程式c跟d使用
且是按照順序的給下去,swap第一個參數的c對應主程式第一個給的引數a
main的a | main的b | swap的c | swap的d | ||
儲存值 | 5 | 10 | …… | 5 | 10 |
記憶體位址 | 0×04 | 0×08 | …… | 0×16 | 0×20 |
當副程式的程式執行完之後記憶體會變如下:
main的a | main的b | swap的c | swap的d | ||
儲存值 | 5 | 10 | …… | 10 | 5 |
記憶體位址 | 0×04 | 0×08 | …… | 0×16 | 0×20 |
最後回到主程式並印出a跟b的值,會發現a跟b根本沒有改變,為什麼!?
這就是因為你使用的方式是傳值call by value,傳值的意思顧名思義就是只把『值』複製給對方
對方拿到了你的值以後做任何改變都不會影響到原本的值,因此如果你想要利用副程式把a跟b交換,可以使用傳址
傳址call by address:
在這段程式碼中,我們在主程式宣告了a跟b的值,同樣見下圖:
main的a | main的b | ||||
儲存值 | 5 | 10 | …… | …… | …… |
記憶體位址 | 0×04 | 0×08 | …… | …… | …… |
接著當下一行我們呼叫了副程式swap(&a,&b),意思就是要把a跟b的位址給swap副程式c跟d使用
&的意思就是要領出該變數的『位址』,因此&a的話是把0×04的記憶體位址給對方
而副程式的swap(int *c , int *d)
其中*的意思是『指標Pointer』指標是用來儲存記憶體位址的,
因此這段程式的意思就是『指標c指向a』執行到副程式後記憶體如下表示:
main的a | main的b | swap的*c | swap的*d | ||
儲存值 | 5 | 10 | …… | 0×04 | 0×08 |
記憶體位址 | 0×04 | 0×08 | …… | 0×16 | 0×20 |
如果你在副程式裡直接執行printf(“%d”,c); 印出來的內容只會是主程式a的記憶體位置,也就是0×04
所以當你想用c來印出a的內容,得用*c才可以印出printf(“%d”,*c);
在這邊*已經不是指標的意思,而是『提領Dereference』,簡單說就是提取本身所指向的位址的值,也就是a的值5
所以副程式執行完交換後記憶體如下表示:
main的a | main的b | swap的*c | swap的*d | ||
儲存值 | 10 | 5 | …… | 0×04 | 0×08 |
記憶體位址 | 0×04 | 0×08 | …… | 0×16 | 0×20 |
所以副程式執行完後,主程式的printf函式就可以順利的印出交換後的a與b。
看到這裡很多人應該都會認為這樣的確是call by address不是嗎?畢竟傳過去的是位址,而不是值
這邊有一段程式碼給大家看看:
當第一個printf執行後,印出來的c跟d都是5
可是下一行的d=&b執行後在印一次,c卻仍然是5,d卻變成了10
因此你可以這樣想:傳指標,仍然也是call by value,只是那個value是指標本身,
複製的內容也是指標本身,只不過那個值Value剛好就是位址address
所以各位要大概瞭解call by address本質上也是call by value或者叫call by value of pointer
不過畢竟這call by address的講法已經在台灣紮根了,所以也不用強硬的堅持C語言只有call by value
總結:
指標是專門用來儲存記憶體的位址,但指標本身也有自己的記憶體位址
int *ptr; 這裡的*號代表指標Pointer,在未指向任何位址的指標,你不可以去提領他,這是非法行為
int *ptr2=NULL 如果指標宣告後一開始還沒打算讓他指向任何東西,良好習慣就是給他一個NULL值
int *ptr3 = &x; &x意思是要把x的記憶體位址傳出來給指標ptr3
printf(“%d”,*ptr3); 這裡的*號代表提領Dereference,意思是要把本身指向的位址的值提領出來
傳參考call by reference:
傳參考是C++才有的東西,C語言是沒有的唷,可以說call by reference是call by address的進化版,
因為傳址的指標它的內容為指向的位址,但他本身仍然有記憶體位址,但是傳參考是不會有的
我們先來看程式碼
當主程式執行後記憶體如下
main的a | main的b | ||||
儲存值 | 5 | 10 | …… | …… | …… |
記憶體位址 | 0×04 | 0×08 | …… | …… | …… |
在呼叫swap(a,b)時像平常一樣直接給引數就好,但副程式那邊必須要把c跟d加上&,變成void swap (int &c , int &d)
PS:有些人會長會被傳參考的&跟上面在講的傳址的&一樣,其實是不一樣的,別搞混
這樣就會是用傳參考的方式來接收主程式a跟b的值,此時記憶體如下所示:
main的a | main的b | swap的&c | swap的&d | ||
儲存值 | 5 | 10 | …… | 5 | 10 |
記憶體位址 | 0×04 | 0×08 | …… | 0×04 | 0×08 |
沒錯,傳參考的意思其實簡單來說就是變數的別名、綽號,因此c的記憶體位址跟主程式的a的位址一樣
因為一樣,所以c做任何改變a也會跟著改變,d改變b也會跟著改變
注意的是,一旦變數已經是別的變數的參考,就不可以再參考別的變數
===================
int a=5;
int &c; //錯誤,傳參考必須在宣告的時候就一起給參考的對象,沒辦法之後再給。
====================================================================
C++的call by reference,其實才算是call by address,來看同樣是上面的例子
==========================
留言列表