【C++ 筆記】函數(Function)(下) - part 11

很感謝你點進來這篇文章。

你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧!

函數參數的傳遞方式

如果函數要使用參數,則必須宣告接受參數值的變數。這些變數稱為函數的形式參數。

形式參數如同局域變數(Local Variable),自從函數被呼叫開始,就被建立,函數程式碼結束後,則形式參數自動被刪除。

呼叫函數時,有三種參數的傳遞方式,如下:

  • 傳值呼叫(call by value)
  • 傳址呼叫(call by address / call by pointer / pass by address)
  • 傳參(考)呼叫(call by reference)

這三種方式有什麼差異呢?請繼續看下去~

傳值呼叫(call by value)

傳值呼叫中,被傳遞的參數實際值會複製給形式參數,因此無論如何修改函數內的形式參數皆不會改變到實際參數值。

這就好像世界上總有許多同名的人,但他們本質上是不同的,比如有很多叫 Amy 的人,但是有的 Amy 很有錢,有的超窮等等。

:::success
Call by value 所引用的變數僅為當時的數值,而非變數本身。
:::

具體而言,看以下的範例即可易懂,這是最常見的 swap() 函數程式碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;

// 函數宣告
void swap(int x, int y);

// 函數定義
void swap(int x, int y)
{
int temp;

temp = x; /* 保存 x 的值 */
x = y; /* 把 y 指定给 x */
y = temp; /* 把 x 指定给 y */

return;
}

int main ()
{
int a = 100;
int b = 200;

cout << "交換前,a 的值:" << a << endl;
cout << "交換前,b 的值:" << b << endl;

// 呼叫函數交換值
swap(a, b);

cout << "交換後,a 的值:" << a << endl;
cout << "交換後,b 的值:" << b << endl;

return 0;
}

輸出結果:

1
2
3
4
交換前,a 的值:100
交換前,b 的值:200
交換後,a 的值:100
交換後,b 的值:200

可見 a、b 值經交換函數後,輸出結果沒有任何變化。

那要如何使 a、b 值交換呢?請繼續看下去。

傳址呼叫(call by address / call by pointer)

傳址呼叫就是將參數的地址複製給形式參數,所以在函數內修改形式參數會影響實際參數。

與傳值調用範例程式碼寫法有些許不同,因為需要將地址複製給形式參數,所以需要使用指標於函數內。

:::success
「傳址呼叫」是將變數的記憶體位址傳遞給函數的參數,而不是直接傳遞變數的值。因此,函數內部可以透過該位址存取並修改外部變數的數值。
:::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
using namespace std;

// 函數宣告
void swap(int *x, int *y);

// 函數定義
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 指定给 x */
*y = temp; /* 把 x 指定给 y */

return;
}

int main ()
{
int a = 100;
int b = 200;

cout << "交換前,a 的值:" << a << endl;
cout << "交換前,b 的值:" << b << endl;

/* 呼叫函數交換值
* &a 表示指向 a 的指標,即變數 a 的地址
* &b 表示指向 b 的指標,即變數 b 的地址
*/
swap(&a, &b);

cout << "交換後,a 的值:" << a << endl;
cout << "交換後,b 的值:" << b << endl;

return 0;
}

輸出結果:

1
2
3
4
交換前,a 的值: 100
交換前,b 的值: 200
交換後,a 的值: 200
交換後,b 的值: 100

可以發現 a、b 值成功交換了。

傳參考呼叫(call by reference)

向函數傳遞參數的傳參呼叫方法,把參考的位址複製給形式參數。在函數內,此參考用於存取呼叫中要用到的實際參數。這意味著,修改形式參數會影響實際參數。

光看定義可能有點不太懂,但其實就只是在參數前面加上”&”而已,參數都不需要像傳址呼叫一樣改成指標,因為加上 & 就表示傳參的記憶體位址。

重點是傳參考呼叫不須使用指標即可達到修改外部變數的目的。

:::success
「傳參考呼叫」是將變數本身的參考傳遞給函數,使函數能直接操作該變數的內容而非其副本。
:::

:::info
參考(Reference)是指一種資料型態,允許程式間接存取記憶體中的特定資料。這些資料可以是變數,參考本身並不包含數據(Data),而是指向數據在記憶體中的位址。

當傳遞參考時,實際上是傳遞了資料的「位址」,而不是資料的副本,因此對資料的修改將直接影響原始數據,所以傳參考呼叫為何能與傳址呼叫具有同樣的效果。
:::

有關於參考的解釋詳情請至我的筆記 part 14:傳送門點我

具體範例請看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;

// 函數宣告
void swap(int &x, int &y);

int main ()
{
int a = 100;
int b = 200;

cout << "交換前,a 的值:" << a << endl;
cout << "交換前,b 的值:" << b << endl;

/* 呼叫函數交換值 */
swap(a, b);

cout << "交換後,a 的值:" << a << endl;
cout << "交換後,b 的值:" << b << endl;

return 0;
}

// 函數定義
void swap(int &x, int &y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 指定给 x */
y = temp; /* 把 x 指定给 y */

return;
}

輸出結果:

1
2
3
4
交換前,a 的值: 100
交換前,b 的值: 200
交換後,a 的值: 200
交換後,b 的值: 100

傳參考呼叫的應用場景:

  • 修改外部變數:如上例所示,透過函數修改傳入的變數。
  • 減少開銷:特別是在傳遞大物件或複雜資料結構時,引用傳遞可以避免不必要的拷貝操作。
  • 傳回值最佳化:透過引用傳回函數的局部變量,避免了傳回值拷貝的開銷。

傳參考呼叫是 C++ 中的重要技術,可以提高程式碼的效率和靈活性。了解和熟練使用引用呼叫對於編寫高效的C++程式非常有幫助。

來源:https://www.runoob.com/cplusplus/cpp-function-call-by-reference.html

總結

解釋:

  • 傳值呼叫(call by value):傳遞參數時引用的不是變數本身,而是它的值,所以並沒有引用到他的記憶體位址。
  • 傳址呼叫(call by address):傳遞參數時將變數的記憶體位址傳遞給函數的參數,而不是直接傳遞變數的值。->所以能改變變數的值
  • 傳參考呼叫(call by reference):將變數本身的參考傳遞給函數,使函數能直接操作該變數的內容而非其副本(不是變數的copy,如果直接改copy值那對初始的變數完全沒影響)。

論其功用而言:

  • 傳值呼叫(call by value):適合小型資料型態,簡單且安全,但不適合大型資料結構,因為會造成效能損耗。
  • 傳址呼叫(call by address):允許直接運算原始變數,適合需要修改多個變數或處理大型資料結構時使用。
  • 傳參考呼叫(call by reference):與傳址類似,但語法更簡潔且不需要手動提取(Dereference),適合需要修改原始變數的情況。

參考資料

【教學】call by value, call by address, call by reference 差別在哪?-橘子亂說話|痞客邦

C++ function call by reference

C++ 函数 | 菜鸟教程

C++ 传值调用 | 菜鸟教程

C++ 指针调用 | 菜鸟教程

C++ 引用调用 | 菜鸟教程