在C/C++中函數的調用有三種傳遞參數的方式,分別是傳值調用,傳址調用,以及引用調用(其中引用調用是C++中擴充的)。在這裡用一個簡單的函數---交換兩個數來說明一下三種調用的區別。
在這之前,我們先來說一些基礎的東西。
第一:我們所編寫的源代碼在經過編譯器編譯之後,所有的變量名都會對應一個內存地址,而我們對這些變量的操作也就是對這些地址的操作。
第二:說明一下&a,a以及*a的不同。這三個可以說是層層深入的關系,舉一個例子來說,假如我們給a分配的內存地址是0x0012ff28,裡面的內容是0x0012ff7c,以輸出為例,我們輸出&a就是直接把0x0012ff28輸出,而輸出a則是把0x0012ff7c輸出,輸出*a則是把0x0012ff7c地址裡面的數據輸出來。其他的操作(比如加減等操作)大致也都是這樣。
好了,接下來就是正題了,讓我們看一下這三種調用方式的區別,這裡我們假設實參的值分別是5和6,地址分別是0x0012ff78和0x0012ff7c:
①傳值調用,swap(int nNum1, int nNum2)
程序在調用這個函數時,先額外申請兩個內存單元0x0012ff24和0x0012ff28,並分別初始化為5和6。然後接下來的操作都是對這兩個地址內存放的數據進行操作,這也就是為什麼在這個函數中所進行的一系列操作都不會改變原來的數據的原因。
②傳址調用,swap(int* pNum1, int* pNum2)
程序在調用這個函數時同樣先分配兩個內存單元0x0012ff24,初始化為0x0012ff78(nNum1地址),以及0x0012ff28,初始化為0x0012ff7c(nNum2地址),以後使用的時候用*pNum1即使用的是0x0012ff78,用*pNum2即使用的是0x0012ff7c,所以傳址調用可以改變原數據。
③引用傳值,swap(int& nNum1, int& nNum2)
程序在調用這個函數時並不重新分配內存空間,而是直接把nNum1對應於0x0012ff78,把nNum2對應於0x0012ff7c。這樣形參和實參實際上就是相同的了。
讓我們從代碼的角度看一下(由於nTemp無關緊要,所以不做分析,至於(0x********)是為了方便理解,表示對地址裡的內容進行操作,這種寫法並不規范,只是為了看起來直觀;而且這裡是假設函數在某次調用的時候進行的分析):
①傳值調用
void swap(int nNum1, int nNum2) void swap( (0x0012ff24), (0x0012ff28) )
{ {
int nTemp; int nTemp;
nTemp = nNum1; nTemp = (0x0012ff24);
nNum1 = nNum2; (0x0012ff24) = (0x0012ff28);
nNum2 = nTemp; (0x0012ff28) = nTemp;
} }
②傳址調用(這裡用三步來描述)
void swap(int* pNum1, int* pnNum2) void swap( (0x0012ff24), (0x0012ff28) )
{ {
int nTemp; int nTemp;
nTemp = *pNum1; nTemp = *(0x0012ff24);
*pNum1 = *pNum2; *(0x0012ff24) = *(0x0012ff28);
*pNum2 = *pTemp; *(0x0012ff28) = nTemp;
} }
void swap( (0x0012ff24), (0x0012ff28) )
{
int nTemp;
nTemp = (0x0012ff78);
(0x0012ff78) = (0x0012ff7c);
(0x0012ff7c) = nTemp;
}
③引用傳值
void swap(int& nNum1, int& nNum2) void swap( (0x0012ff78), (0x0012ff7c) )
{ {
int nTemp; int nTemp;
nTemp = nNum1; nTemp = (0x0012ff78);
nNum1 = nNum2; (0x0012ff78) = (0x0012ff7c);
nNum2 = nTemp; (0x0012ff7c) = nTemp;
} }