【不是惡夢的開始,而是再度的精進。 傳值、傳址、傳參考】

 

在傳遞引數給函數時,有著很繞口的傳值、傳址、傳參考,也因為繞口,就變得很容易混淆觀念,因此就用同一個例子來看看傳值、傳址、傳參考到底有多少差別。


就先來看看傳值的方式:


#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);
這樣是不是通順多了....


如果你對傳址的方式也是卡卡的,多轉了這麼一個步驟,是不是有幫助呢?希望沒把你給弄更亂....
當然啦~~也要適應大家的寫作方式,這樣在運用別人的程式才不會心驚驚腦茫茫

 

 

 

 

.

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 伊蒙‧普羅客 的頭像
    伊蒙‧普羅客

    面向陽光,陰影就會在身後

    伊蒙‧普羅客 發表在 痞客邦 留言(4) 人氣()