在C/C++語言中值傳遞、指針傳遞和引用傳遞(C++ only)這三種函數參數傳遞方式是比較基本的知識,用的比較普遍,但不仔細分析其實質的話,時間長容易記混了。網上的資料也較多但多數都不繫統,本文力求用最白話的表達和簡單的示例把這三種方式描述清楚。沒時間看分析的直接看簡述就可以了。
簡述
值傳遞就是最普通的傳遞方式,比如函數定義爲fun(int a)
,在調用的地方有int x=6
, 使用fun(x)
就可以了。這種方式在fun(int a)
函數內部的對a的修改 不能 導致外部x的變化。
指針傳遞其實也就是地址傳遞,函數定義爲fun(int *a)
,形參爲指針,這就要求調用的時候傳遞進去一個參數的地址,例如int x=6;
fun(&x)
。 這種方式在fun(int a)
函數內部的對a的修改 能 導致外部x的變化。
引用傳遞只有C++支持,相比前兩種方式用的比較少,但也非常有用。引用傳遞函數定義爲fun(int &a)
,這裏&符號是引用而不是取地址的意思,調用方式和值傳遞類似,例如int x=6;
fun(x)
。 但是這種方式在fun(int a)函數內部的對a的修改 能 導致外部x的變化。
表格是一種比較直接的表達方式,列個表格可以對這三種方式一目瞭然,假設調用fun函數之前都有定義int x=6,這三種方式異同如下表所示:
傳遞方式 | 函數定義 | 函數調用 | 函數內對a修改的影響 |
---|---|---|---|
值傳遞 | fun(int a) | fun(x) | 外部x不變 |
指針傳遞 | fun(int *a) | fun(&x) | 外部x同步更改 |
引用傳遞(C++) | fun(int &a) | fun(x) | 外部x同步更改 |
分析
下面三部分代碼都嘗試在swap函數內對輸入參數進行數值交換,通過打印其數值和指針來分析。
值傳遞
函數內部使用這個參數,對這個參數的修改對函數外的原始數據不起作用。
示例:
#include <stdio.h>
void swap(int a, int b){
printf("swap enter\n");
printf("a = %d, ptr = %p\n", a, &a);
printf("b = %d, ptr = %p\n", a, &b);
int tmp = b;
b = a;
a = tmp;
printf("a = %d, ptr = %p\n", a, &a);
printf("b = %d, ptr = %p\n", b, &b);
printf("swap leave\n");
}
int main() {
int x = 1;
int y = 2;
printf("x = %d, ptr = %p\n", x, &x);
printf("y = %d, ptr = %p\n", y, &y);
swap(x, y);
printf("x = %d, ptr = %p\n", x, &x);
printf("y = %d, ptr = %p\n", y, &y);
return 0;
}
運行結果:
x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48
swap enter
a = 1, ptr = 000000000022FE20
b = 1, ptr = 000000000022FE28
a = 2, ptr = 000000000022FE20
b = 1, ptr = 000000000022FE28
swap leave
x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48
結果表明:
swap函數內部a b
的數值進行交換,但是沒有影響到x y
的值。swap函數形參a b
所指向的內存地址和外部x y
的地址不一樣。
實質:
swap爲形式參數 a b
創建了內存空間,main函數調用swap函數的時候,把x y
的值copy給新創建的 a b
。a b
和x y
分別在不同的內存位置中,因而對這個新創建的 a b
操作不會影響外部的 x y
的值。
帶有返回值的函數,比如
int fun(){int x=7; return x}
,外部調用方式爲int a=fun();
,這時是把x的數值copy了一份給外部a所在的內存空間,並釋放了x所在的內存空間(系統自動分配的棧內存),在fun函數調用後即使知道x的內存地址也可能取不到相應的數據。
返回值爲指針的函數,比如char *fun(){...; return str;}
,函數中待返回的變量必須是使用malloc等函數手動申請空間(堆內存)的數據,且外部使用後要手動釋放。因爲系統自動申請的棧內存會在函數調用後自動釋放。
指針傳遞(地址傳遞)
形參爲指向實參地址的指針,當對形參的指向操作時,就相當於操作實參本身。
示例:
#include <stdio.h>
void swap(int *a, int *b){
printf("swap enter\n");
printf("a = %d, ptr = %p\n", *a, a);
printf("b = %d, ptr = %p\n", *a, b);
int tmp = *b;
*b = *a;
*a = tmp;
printf("a = %d, ptr = %p\n", *a, a);
printf("b = %d, ptr = %p\n", *b, b);
printf("swap leave\n");
}
int main() {
int x = 1;
int y = 2;
printf("x = %d, ptr = %p\n", x, &x);
printf("y = %d, ptr = %p\n", y, &y);
swap(&x, &y);
printf("x = %d, ptr = %p\n", x, &x);
printf("y = %d, ptr = %p\n", y, &y);
return 0;
}
運行結果:
x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48
swap enter
a = 1, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
a = 2, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
swap leave
x = 2, ptr = 000000000022FE4C
y = 1, ptr = 000000000022FE48
結果表明:
swap函數內部a b
的數值進行交換,外部的x y
的值也發生了交換。swap函數形參a b
所指向的內存地址和外部x y
的地址分別都相同。
實質是:
swap函數形式參數其實是一個指針,可能把fun(int *a)
寫成fun(int* a)
就比較容易理解了,形參a爲地址指針。*a
就是x
,a
就是&x
。函數內部要想修改其數值就要使用*a
取數值,打印其地址直接打印a
就可以了。
函數內部對a b
的操作實際上是操作x y
所在的內存空間,所以函數內的修改會影響到外部x y
的數值。
引用傳遞
引用傳遞的形參加一個&符號,這個形參相當於實參的一個別名,對形參的操作都相當於對實參的操作。
注意形參帶&符號的引用傳遞在C語言中是不可用的,只有C++中支持。
C語言中函數參數總是通過值傳遞,可以通過顯式傳遞指針值模擬引用傳遞。
Function parameters are always passed by value. Pass-by-reference is simulated in C by explicitly passing pointer values. – https://en.wikipedia.org/wiki/C_(programming_language)
示例:
#include <stdio.h>
void swap(int &a, int &b){
printf("swap enter\n");
printf("a = %d, ptr = %p\n", a, &a);
printf("b = %d, ptr = %p\n", a, &b);
int tmp = b;
b = a;
a = tmp;
printf("a = %d, ptr = %p\n", a, &a);
printf("b = %d, ptr = %p\n", b, &b);
printf("swap leave\n");
}
int main() {
int x = 1;
int y = 2;
printf("x = %d, ptr = %p\n", x, &x);
printf("y = %d, ptr = %p\n", y, &y);
swap(x, y);
printf("x = %d, ptr = %p\n", x, &x);
printf("y = %d, ptr = %p\n", y, &y);
return 0;
}
運行結果:
x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48
swap enter
a = 1, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
a = 2, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
swap leave
x = 2, ptr = 000000000022FE4C
y = 1, ptr = 000000000022FE48
結果表明:
函數內對形參的使用方式和值傳遞相同,產生的效果和指針傳遞相同。
swap函數內部a b
的數值進行交換,外部的x y
的值也發生了交換。swap函數形參a b
所指向的內存地址和外部x y
的地址分別都相同。
實質是:
形式參數前加&符號,這個形參相當於實參的一個別名,對這個形參的操作等同於對實參的操作,有點類似快捷方式。a
等同於x
,比如fun(int &a)
函數內對a的操作和值傳遞的函數fun(int a)
中對形參的操作一樣,但是修改形參同時也是修改實參。