值傳遞和地址傳遞,C語言函數傳參方式詳解

2020-07-16 10:04:23
在 C 語言中,函數的引數傳遞方式有兩種:值傳遞與地址傳遞。下面分別介紹這兩種傳遞形式。

值傳遞

這種方式使用變數、常數、陣列元素作為函數引數,實際是將實參的值複製到形參相應的儲存單元中,即形參和實參分別佔用不同的儲存單元,這種傳遞方式稱為“引數的值傳遞”或者“函數的傳值呼叫”。

值傳遞的特點是單向傳遞,即主調函數呼叫時給形參分配儲存單元,把實參的值傳遞給形參,在呼叫結束後,形參的儲存單元被釋放,而形參值的任何變化都不會影響到實參的值,實參的儲存單元仍保留並維持數值不變。

來看下面一個呼叫範例:
#include <stdio.h>
/* 變數x、y為Swap函數的形式引數 */
void Swap(int x, int y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
    printf("x = %d, y = %dn", x, y);
}
int main(void)
{
    int a=10;
    int b=20;
     /*變數a、b為Swap函數的實際引數*/
    Swap(a, b);
    printf("a = %d, b = %dn", a, b);
    return 0;
}
在上面這個範例程式碼中,實參將值傳遞給形參,形參值發生互換後的值不能回傳給主調函數。因此,主調函數中的數值不變,程式碼的執行結果為:
x = 20, y = 10
a = 10, b = 20

對於上面這個範例,或許有人會有如下疑問:上面的範例中明確地把 a、b 分別代入了 x、y 中,並在函數 Swap() 裡完成了兩個變數值的交換,為什麼 a、b 變數值還是沒有交換。其結果仍然是“a=10,b=20”,而不是“a=20,b=10”呢?

其實,原因很簡單。函數在呼叫時,隱含地把實參 a 的值賦值給了引數 x,而將實參 b 的值賦值給了引數 y,如下面的程式碼所示:
/*將a的值賦值給x(隱含動作)*/
int x = a;
/*將a的值賦值給y(隱含動作)*/
int y = b;
因此,之後在 Swap() 函數體內再也沒有對 a、b 進行任何操作。而在 Swap() 函數體內交換的只是 x、y,並不是 a、b,當然,a、b 的值沒有改變。整個 Swap() 函數呼叫是按照如下順序執行的:
/*將a的值賦值給x(隱含動作)*/
int x = a;
/*將a的值賦值給y(隱含動作)*/
int y = b;
int tmp;
tmp = x;
x = y;
y = tmp;
printf("x = %d, y = %dn", x, y);
由此可見,函數只是把 a、b 的值通過賦值傳遞給 x、y,在函數 Swap() 中操作的只是 x、y 的值,並不是 a、b 的值,這也就是所謂的引數的值傳遞。

地址傳遞

這種方式使用陣列名或者指標作為函數引數,傳遞的是該陣列的首地址或指標的值,而形參接收到的是地址,即指向實參的儲存單元,形參和實參佔用相同的儲存單元,這種傳遞方式稱為“引數的地址傳遞”。

地址傳遞的特點是形參並不存在儲存空間,編譯系統不為形引數組分配記憶體。陣列名或指標就是一組連續空間的首地址。因此在陣列名或指標作函數引數時所進行的傳送只是地址傳送,形參在取得該首地址之後,與實參共同擁有一段記憶體空間,形參的變化也就是實參的變化。

來看下面一個呼叫範例:
void Swap(int *px, int *py)
{
    int tmp;
    tmp = *px;
    *px = *py;
    *py = tmp;
    printf("*px = %d, *py = %dn", *px, *py);
}
int main(void)
{
    int a=10;
    int b=20;
    Swap(&a, &b);
    printf("a = %d, b = %dn", a, b);
    return 0;
}
在上面的範例程式碼中,函數 void Swap(int*px,int*py) 中的引數 px、py 都是指標型別,在 main 函數中使用語句“Swap(&a,&b)”進行呼叫,該呼叫語句將 a 的地址(&a)代入 px,b 的地址(&b)代入 py。很顯然,這裡的函數呼叫有兩個隱含操作:將 &a 的值賦值給引數 px,將 &b 的值賦值給引數 py,如下面的程式碼所示:
px = &a;
py = &b;
注意,這裡與值傳遞方式存在著很大的區別。在值傳遞方式中,傳遞的是變數 a、b 的內容(即在上面的值傳遞範例程式碼中,將 a、b 的內容傳遞給引數 x、y);而這裡的地址傳遞方式則是將變數 a、b 的地址值(&a、&b)傳遞給引數 px、py。因此,整個 Swap() 函數呼叫是按照如下順序執行的:
/*將&a的值賦值給px(隱含動作)*/
px = &a; /* ← */
/*將&b的值賦值給py(隱含動作)*/
py = &b;
int tmp;
tmp = *px;
*px = *py;
*py = tmp;
printf("*px = %d, *py = %dn", *px, *py);
這樣,有了前兩行的隱含賦值操作,指標變數 px、py 的值已經分別是變數 a、b 的地址值(&a、&b)。接下來,對“*px”“*py”的操作當然也就是對 a、b 變數本身的操作了。所以 Swap() 函數中的交換操作就是對 a、b 值進行交換,這就是所謂的地址傳遞,執行結果為:
*px = 20, *py = 10
a = 20, b = 10