什麼是傳值call by value、傳址call by address、傳參考call by reference

何為傳值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 …… …… ……
記憶體位址 0x04 0x08 …… …… ……

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
記憶體位址 0x04 0x08 …… 0x16 0x20

當副程式的程式執行完之後記憶體會變如下:

main的a main的b swap的c swap的d
儲存值 5 10 …… 10 5
記憶體位址 0x04 0x08 …… 0x16 0x20

最後回到主程式並印出a跟b的值,會發現a跟b根本沒有改變,為什麼呢?

這就是因為你使用的方式是傳值call by value,傳值的意思顧名思義就是只把”值”複製給對方

對方拿到了你的值以後做任何改變都不會影響到原本的值,因此如果你想要利用副程式把a跟b交換,可以使用傳址

傳址call by address:

在這段程式碼中,我們在主程式宣告了a跟b的值,同樣見下圖:

main的a main的b
儲存值 5 10 …… …… ……
記憶體位址 0x04 0x08 …… …… ……

接著當下一行我們呼叫了副程式swap(&a,&b),意思就是要把a跟b的位址給swap副程式c跟d使用

&的意思就是要領出該變數的”位址”,因此&a的話是把0x04的記憶體位址給對方

而副程式的swap(int *c , int *d)

其中*的意思是”指標Pointer”指標是用來儲存記憶體位址的,

因此這段程式的意思就是”指標c指向a”,執行到副程式後記憶體如下表示:

main的a main的b swap的*c swap的*d
儲存值 5 10 …… 0x04 0x08
記憶體位址 0x04 0x08 …… 0x16 0x20

如果你在副程式裡直接執行printf(“%d”,c); 印出來的內容只會是主程式a的記憶體位置,也就是0x04

所以當你想用c來印出a的內容,得用*c才可以印出printf(“%d”,*c);

在這邊*已經不是指標的意思,而是”提領Dereference”,簡單說就是提取本身所指向的位址的值,也就是a的值5

所以副程式執行完交換後記憶體如下表示:

main的a main的b swap的*c swap的*d
儲存值 10 5 …… 0x04 0x08
記憶體位址 0x04 0x08 …… 0x16 0x20

所以副程式執行完後,主程式的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 …… …… ……
記憶體位址 0x04 0x08 …… …… ……

在呼叫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
記憶體位址 0x04 0x08 …… 0x04 0x08

由上表可以得知,傳參考的意思其實簡單來說就是變數的別名、綽號,因此c的記憶體位址跟主程式的a的位址一樣

因為一樣,所以c做任何改變a也會跟著改變,d改變b也會跟著改變

注意的是,一旦變數已經是別的變數的參考,就不可以再參考別的變數

 

====================================================================

C++的call by reference,其實才算是call by address,來看下面的例子

==========================

感謝網友修正以下程式碼

002

12 comments

  1. 大大不好意思

    最後一個範例有點問題喔

    const int *x=&a;  是指到的值不能被改變

    所以還是會compiler error

    應該改成int * const x=&a; 才是你要的資料

     

     

     

  2. 感謝你的指教,真的如你講的才是正確的,

    已經修正文章了,再次感謝你

  3. 謝謝您的講解,非常清楚(以前真的被很多教材誤導)! 可是請問一下最後的call by reference 為何執行完a和b的值還是沒變呢?

  4. 那裡說的”此時”指的是值在swap函數中的狀況, 並不是執行swap函數後的狀況, 所以還沒改變.

  5. 這篇好讚,請問還有其他傳遞方式的教學嗎?

    Pass-by-result

    Pass-by-value-result

    Pass-by-name

  6. 真的非常謝謝你的講解,比我那本Java – How to Program還詳細!!

Leave a Reply

你的電子郵件位址並不會被公開。 必要欄位標記為 *