【不是惡夢的開始,而是再度的精進。 傳值、傳址、傳參考】
在傳遞引數給函數時,有著很繞口的傳值、傳址、傳參考,也因為繞口,就變得很容易混淆觀念,因此就用同一個例子來看看傳值、傳址、傳參考到底有多少差別。
就先來看看傳值的方式:
#include <iostream>
using namespace std;
int add(int initial);
int main(int argc, char** argv)
{
int ai = 9;
int resulti = add(ai); // 使用變數
cout << "\n\n呼叫完add()函數後 main()函數內 ai = " << ai;
cout << "\n\nai在記憶體中的位址 &ai = " << &ai;
cout << "\n\n回傳給resulti的值是 " << resulti;
return 0;
}
int add(int ci) // ci 是拷貝自 ai
{
ci += 3;
cout << "\n\n在add函數中 ci = " << ci;
cout << "\n\nci在記憶體中的位址 &ci = " << &ci;
return ci;
}
執行結果:
在add函數中 ci = 12
ci在記憶體中的位址 &ci = 0x70fde0 // 和ai不是同一個記憶體,因此ci是拷貝自ai的另一個變數
呼叫完add()函數後 main()函數內 ai = 9
ai在記憶體中的位址 &ai = 0x70fe0c
回傳給resulti的值是 12
很明顯的,在main()函數內的變數ai的值一直保持著為9,在add()函數內的ci已經改變為12了,這是因為以傳值的方式來呼叫函數時,是把引數ai複製一份給add()函數,因此main()函數內和add()函數內各有一份自己的變數ai和ci在記憶體內,因此add()函數並沒有更動到main()函數內的變數ai。
接著來看看寫法和傳值很類似的傳參考,雖然長得很像卻也是不一樣。
#include <iostream>
using namespace std;
int add(int& initial);
int main(int argc, char** argv)
{
int ai = 9;
int resulti = add(ai); // 使用變數
cout << "\n\n呼叫完add()函數後 main()函數內 ai = " << ai;
cout << "\n\nai在記憶體中的位址 &ai = " << &ai;
cout << "\n\n回傳給resulti的值是 " << resulti;
return 0;
}
int add( int& ci) // ci 是參考自 ai
{
ci += 3;
cout << "\n\n在add函數中 ci = " << ci;
cout << "\n\nci在記憶體中的位址 &ci = " << &ci;
return ci;
}
執行結果:
在add函數中 rci = 12
rci在記憶體中的位址 &rci = 0x70fe0c // 和ai是同一個記憶體,因此rci是ai的別名
呼叫完add()函數後 main()函數內 ai = 12
ai在記憶體中的位址 &ai = 0x70fe0c
回傳給resulti的值是 12
和傳值不一樣的地方就是宣告函數原型的地方加一個宣告參考&,這個'&'並不是取直運算子。
宣告參考的&和取址的&該怎麼分別呢?我用下列式子試著說明
int ai = 5;
int* p = &ai; // 在等號的右左邊為取址運算子
int& rci = ai; // 在等號的左邊為宣告參考,又或者說&跟在int、double、char等等宣告變數型態後面的就是宣告參考
我們可以這樣說,參考就是個別名,所以宣告參考值時,一定要用變數名稱作初值化。在這個例子中rci和ai是同一個位址(0x70fe0c)的變數,只是不同名稱而已,因此當我們更改了變數rci(別名)的值,也就更改了變數ai(本名)的值。
比較一下在傳參考和傳值的寫法,也只是在傳引數的描述不一樣,至於函數主體的寫法都一樣,但就會影響到原變數ai了,因此若ai在呼叫函數後不該被改變時,就不可使用傳參考來呼叫函數。
接著來看看指標傳址的寫法:
#include <iostream>
using namespace std;
int add(int* initial);
int main(int argc, char** argv)
{
int ai = 9;
int resulti = add(&ai);
cout << "\n\n呼叫完add()函數後 ai = " << ai;
cout << "\n\n回傳給resulti的值是 " << resulti;
return 0;
}
int add(int* pi_ai)
{
*pi_ai += 3;
cout << "\n\n在add函數中 *pi_ai = " << *pi_ai;
return *pi_ai;
}
執行結果:
在add函數中 *pi_ai = 12
呼叫完add()函數後 ai = 12
回傳給resulti的值是 12
以傳址來呼叫函數我最體會不過來的地方,就是明明宣告函數 int add(int* pinitial);就是指標,但呼叫函數int resulti = add(&ai);卻是取址而不是給指標,讀別人寫的程式時總是卡卡的,讓自己寫也就一片茫然,糟糕的是大家都這麼寫。或許你會說傳參考不也是一樣嗎?老實說,我以前還真不知道有傳參考這一回事哩!這是我這次重讀C++才發現居然有這種東西。
言歸正傳,讓我們看一下我比較能理解傳址的寫法:
#include <iostream>
using namespace std;
int add(int* pi_initial);
int main(int argc, char** argv)
{
int ai = 9;
int* prt = &ai; // 建議宣告指標時就作初值化
int resulti = add(prt);
cout << "\n\n呼叫完add()函數後 ai = " << ai;
cout << "\n\n回傳給resulti的值是 " << resulti;
return 0;
}
int add(int* pi_ai)
{
*pi_ai += 3;
cout << "\n\n在add函數中 *piai = " << *pi_ai;
return *pi_ai;
}
執行結果:
在add函數中 *pi_ai = 12
呼叫完add()函數後 ai = 12
回傳給resulti的值是 12
如果多了這一到動作 int* prt = &ai;
然後給指標 int resulti = add(prt);
這樣是不是通順多了....
如果你對傳址的方式也是卡卡的,多轉了這麼一個步驟,是不是有幫助呢?希望沒把你給弄更亂....
當然啦~~也要適應大家的寫作方式,這樣在運用別人的程式才不會心驚驚腦茫茫
.