caller a副本 a caller b別名 b 複製同一格
PREREQ P3

函式:傳值、傳參考傳指標

先備知識 · 語法與互動
f(T x)|f(T& x)|f(T* p)|const T&|多載|遞迴
向下捲動開始互動
📌 本頁使用方式(先備知識 · 語法與互動)

先讀語法與例子,再操作互動圖解並完成四選一自測。忘記寫法時回看速查表,最後用中英詞彙卡複習。

CONTENTS · 內容目錄
PART 00 · 函式外觀

先看懂函式的定義與呼叫

函式把一段工作取名字,呼叫時交給它輸入,完成後可把結果交回。先認識每個語法位置,再比較後面的傳值、參考與指標。

int deliveryFee(int count) 中,第一個 int 是回傳型別,deliveryFee 是名稱,int count 是參數。大括號是本體;return 結束呼叫並回傳值。呼叫 deliveryFee(3) 時,引數 3 交給 count;函式不會因定義寫在 main 前面就先執行。

#include <iostream> int deliveryFee(int count) { return count * 12; } int main() { int fee = deliveryFee(3); std::cout << fee << '\n'; }
預期輸出
36

呼叫回來後,fee 取得 36,main 再顯示它。void 表示不回傳值;cout 顯示文字與 return 交出結果是不同動作。下一節再看宣告與定義如何安排。

PART 01 · 宣告與定義

編譯器由上而下讀一遍,讀到才算數

C++ 在編譯一個來源檔時,函式呼叫處必須已看得到相符的宣告。若定義放在 main 後面,就先在前面寫函式原型,讓編譯器能檢查引數與回傳型別。

定義寫在 main 後面,直接編不過
#include <iostream> using namespace std; int main() { cout << square(5) << endl; // 編譯器讀到這裡時還沒見過 square return 0; } int square(int n) { return n * n; }
預期輸出
[編譯失敗]
cell.cpp: In function ‘int main()’:
cell.cpp:5:13: error: ‘square’ was not declared in this scope
    5 |     cout << square(5) << endl;   // 編譯器讀到這裡時還沒見過 square

error: ‘square’ was not declared in this scope。這是初學最常收到的錯誤訊息之一,意思很單純:你用了一個編譯器還沒認識的名字

解法是先寫一行函式原型(function prototype),把回傳型別、名稱、參數型別交代清楚,結尾用分號。這一行就是宣告;有函式本體的那一份才是定義。

加一行宣告就過了
#include <iostream> using namespace std; int square(int n); // 宣告(函式原型):先告訴編譯器它長什麼樣 int main() { cout << "square(5) = " << square(5) << endl; return 0; } int square(int n) { // 定義:實際的程式碼可以擺在後面 return n * n; }
預期輸出
square(5) = 25

宣告只需要型別,參數名可以省略:int square(int); 一樣合法。編譯器需要函式名稱、參數型別與回傳型別來檢查呼叫;參數名稱不影響函式型別。

宣告 declaration定義 definition
長相int square(int n);int square(int n) { ... }
作用告訴編譯器這個名字的長相提供實際的程式碼
可以出現幾次幾次都行本頁的一般函式在整個程式中只有一份定義
放哪個檔.hpp.cpp
💡 為什麼模板定義常放在 .hpp 裡

一般的分工是宣告放標頭檔、定義放 .cpp;引入標頭後,其他檔案就能檢查函式呼叫。模板是另一種情況:編譯器通常需要看見完整定義才能依使用型別產生版本,因此模板定義常放在標頭。P8、P9 再分別介紹多檔組織與模板。

PART 02 · 傳值

傳值:函式拿到的是副本

void f(int n) 這種寫法叫傳值(pass by value)。呼叫的瞬間,編譯器把你給的東西整個抄一份放進函式的參數裡。函式接下來怎麼折騰那份副本,外面那個都不會知道。

void addOne(int v) { // v 是一份全新的副本 v = v + 1; // 改的是副本 } // 函式結束,副本消失 int score = 60; addOne(score); // score 還是 60

這個規則沒有例外,而且對物件也一樣。物件被複製時,編譯器會呼叫它的複製建構式(copy constructor)。下面這支程式把複製這件事印出來給你看。

傳值複製自訂物件時,會由型別決定複製工作;完整複製建構式示範放在 P8 類別與物件,先理解本頁的副本與別名即可。

傳值的時機理由
參數是 intdoublecharbool 這種小東西介面簡單,通常不需為避免小型值複製而改用參考
函式本來就想要一份自己的,改了不該影響呼叫端複製本身就是你要的效果
大物件別用傳值。用 const T&
💡 Python 沒有這個問題,但也沒有這個選擇

Python 傳的一律是物件的參考,所以一個 list 傳進函式,函式裡 append 一下外面就跟著變;整數則因為不可變而看起來像傳值。C++ 把這件事交給你在參數列上明講:抄一份、還是給本人。講清楚的代價是多打幾個字,好處是讀程式的人光看簽章就知道這個函式會不會動到你的資料。

PART 03 · 傳參考

傳參考:同一個東西的另一個名字

int &r = x; 讀作「r 是 x 的參考」。在語言語意上,r 是既有物件 x 的別名,而不是可改指向的指標;編譯器如何實作參考、是否需要額外儲存空間,不能由這項語意推定。

參考與本尊住同一個門牌
#include <iostream> using namespace std; int main() { int x = 10; int &r = x; // r 不是新變數,它就是 x 的另一個名字 r = 99; cout << "改了 r 之後 x = " << x << endl; cout << "x 的位址 = " << &x << endl; cout << "r 的位址 = " << &r << " <- 完全一樣" << endl; cout << "sizeof(x)=" << sizeof(x) << " sizeof(r)=" << sizeof(r) << endl; return 0; }
預期輸出
改了 r 之後 x = 99
x 的位址 = 0x7ffe931b930c
r 的位址 = 0x7ffe931b930c   <- 完全一樣
sizeof(x)=4  sizeof(r)=4

r 取址得到的是被參考物件 x 的位址,因此兩個位址相同;sizeof(r) 量到的也等同 sizeof(x)。這些結果不能用來推定參考本身是否需要額外儲存空間。對照 P4 的指標:指標是保存位址的物件,其大小依平台而異。

參數列上寫 T&,函式收到的就是呼叫端那個變數本人:

一個 & 的差別
#include <iostream> using namespace std; void addOneWrong(int v) { v = v + 1; } // 漏了 & void addOneRight(int &v) { v = v + 1; } // 有 & int main() { int score = 60; addOneWrong(score); cout << "呼叫 addOneWrong 之後: " << score << endl; addOneRight(score); cout << "呼叫 addOneRight 之後: " << score << endl; return 0; }
預期輸出
呼叫 addOneWrong 之後: 60
呼叫 addOneRight 之後: 61

兩個函式的本體完全相同,只有參數列差一個 &,結果一個做白工、一個真的加到了。編譯這支程式時 g++ 一個字都不會抱怨——addOneWrong 在語法上完全合法,它只是沒有用。

傳值 void f(T x)傳參考 void f(T& x)
函式拿到什麼一份副本呼叫端那個變數本人
改得動外面嗎改不動改得動
複製成本整個物件抄一遍
呼叫端寫法f(x);f(x); ← 看不出差別
可以傳 3、傳 a+b 嗎可以不行,非 const 參考只收得下有名字的變數
⚠️ 呼叫端看不出來

f(x); 這一行,無論 f 收的是值還是參考,寫法完全一樣。所以你必須去看函式簽章才知道自己的變數會不會被改掉。指標就沒這個問題:f(&x) 那個 & 明擺在呼叫端。這是下一節要談的取捨。

隨堂 1 · 少了一個符號

如果把調整分數的函式寫成 void adjustScores(vector<int> v)(沒有 &),呼叫 adjustScores(data) 後再印 data,函式內的修改會留在原資料嗎?

(A) 編譯錯誤
(B) 原本的順序,完全沒排到
(C) 排好序的結果
(D) 執行時當掉

先辨認這三個位置:int value=3; 建立整數;&value 取得位址;int* p=&value; 建立保存位址的指標,*p 才代表原整數。下面先用這個最小模型讀參數,完整記憶體圖在 P4。

PART 04 · 傳指標

傳指標:明著把位址交出去

第三種寫法是 void f(T* p),呼叫時寫 f(&x)。函式收到一個位址,用 *p 存取目標。與參考參數相比,指標還能以 nullptr 表示沒有目標,也能在函式內改為指向別處。

指標做得到、參考做不到的兩件事
#include <iostream> using namespace std; void tryFill(int *p) { if (p == nullptr) { // 指標可以為空,所以要檢查 cout << " 收到 nullptr,什麼都不做" << endl; return; } *p = 42; } void mustFill(int &r) { // 參考保證後面有東西,不用檢查 r = 42; } int main() { int a = 0; tryFill(&a); cout << "tryFill(&a) 之後 a = " << a << endl; tryFill(nullptr); cout << "傳 nullptr 進去會安全返回" << endl; int b = 0; mustFill(b); cout << "mustFill(b) 之後 b = " << b << endl; int c = 1, d = 2; int *p = &c; p = &d; // 指標可以改指向別的東西 cout << "p 改指向 d 之後 *p = " << *p << endl; return 0; }
預期輸出
tryFill(&a)  之後 a = 42
  收到 nullptr,什麼都不做
傳 nullptr 進去會安全返回
mustFill(b)  之後 b = 42
p 改指向 d 之後 *p = 2

tryFill(nullptr) 是合法呼叫,函式檢查後安全返回。參考沒有一般的空值狀態,適合表達參數必須綁定有效物件的介面;物件生命週期仍由呼叫端負責。最後兩行展示指標可以改指向另一個目標。

🎯 選哪一個

目標一定存在且需要修改時,參考常能清楚表達需求;允許「沒有目標」或需要描述位址時可用指標。注意指標參數本身也是副本,改函式內的指向不會自動改呼叫端指標。完整比較見 P4

三種傳法,一次看清楚

下面這個逐步器跑的就是開場那支程式。左邊是兩個函式框(呼叫端與被呼叫端)在記憶體裡的樣子,右邊的程式碼會同步高亮。三個按鈕換傳法,注意被呼叫端那兩格是新的還是舊的

選一種傳法開始。三個版本的函式本體完全一樣。
速度
程式碼 CODE
void swapByValue(int a, int b) { int t = a; a = b; b = t; } int x = 3, y = 7; swapByValue(x, y);
看哪裡 KEY

傳值:callee 那格是新配的,位址不同。
傳參考:callee 那格根本沒有新配,a 只是 x 的另一個標籤。
傳指標:callee 多了兩格,裡面裝的是 xy 的門牌。

PART 05 · const 參考

const T&:不複製,也不讓你改

前面兩節各有一項限制:傳值可能產生複製成本,傳參考則允許函式改動呼叫端資料。const T& 結合兩者需要的特性:參數不複製,而且編譯器禁止函式透過它修改資料。

void print(const string &s) { // 不複製、不能改 cout << s << endl; // s = "x"; // 這行會編譯錯 }
試著改一個 const 參考
#include <iostream> using namespace std; void peek(const int &v) { v = 99; // const 參考不准改 } int main() { int x = 3; peek(x); return 0; }
預期輸出
[編譯失敗]
cell.cpp: In function ‘void peek(const int&)’:
cell.cpp:5:7: error: assignment of read-only reference ‘v’
    5 |     v = 99;                 // const 參考不准改

error: assignment of read-only reference。這是編譯期就擋下來的,不是跑到一半才出事。const 在 C++ 是一個會被檢查的承諾,不是註解。

三種傳法的決策表(先用這張挑)

你想做的事寫法複製嗎改得動嗎
參數很小,函式想要一份自己的void f(int n)只改副本
要改動呼叫端的資料void f(T& x)不會
只是要讀一個大物件void f(const T& x)不會不會
參數可能是「沒有」,或要換目標void f(T* p)不會

第三列是傳大物件的標準做法。看到 const vector<int>& v 這種簽章不用緊張,它只是在說「我要看你的資料,我保證不動它,而且我不想抄一份」。

物件成員函式後面的 const 必須先理解類別;原有成功與失敗對照完整放在 P8

PART 06 · 回傳

回傳:可以給值,也可以給仍然有效的參考

回傳值讓呼叫端取得獨立的結果,所以回傳一般區域變數的值可以是安全的。對類別型別,實際可能複製、移動或省略複製,不能推論每次 return 都有一份額外複製。回傳 T& 則提供原物件的參考,使用時必須確認該物件仍存活。

回傳參考 T& 就不一樣了。它交出去的是「那個東西本人」,所以那個東西必須在函式結束後還活著。

回傳參考的正當用法:目標是仍存活的呼叫端變數
#include <iostream> using namespace std; int& currentValue(int& value) { return value; } int main() { int count = 0; currentValue(count) = 5; currentValue(count) += 3; cout << "count = " << count << endl; return 0; }
預期輸出
count = 8

currentValue 回傳 main 中 count 的參考,原物件在呼叫結束後仍存在,所以呼叫結果可作為賦值左側。不能把它改成回傳函式內區域變數的參考。

絕對不能做的事

回傳區域變數的位址參考。區域變數住在呼叫堆疊上,函式一 return,那塊空間就還給系統了,你交出去的是一張過期的門牌。

g++ 有警告,而且是預設就開的
#include <iostream> using namespace std; int *makeNumber() { int local = 1234; // 住在堆疊上,函式一結束就沒了 return &local; // 回傳一個馬上要失效的位址 } int main() { int *p = makeNumber(); cout << "p 拿到的位址 = " << p << endl; cout << "順著它讀出來 = " << *p << endl; return 0; }
g++ 的警告
cell.cpp: In function ‘int* makeNumber()’:
cell.cpp:6:12: warning: address of local variable ‘local’ returned [-Wreturn-local-addr]
    6 |     return &local;           // 回傳一個馬上要失效的位址
      |            ^~~~~~
cell.cpp:5:9: note: declared here
    5 |     int local = 1234;        // 住在堆疊上,函式一結束就沒了
      |         ^~~~~

-Wreturn-local-addr 這個警告不需要 -Wall 就會出現。看到 warning 不要跳過——C++ 的警告訊息裡,這一類幾乎百分之百是真的錯。

那它跑起來會怎樣?
實際執行輸出
錯誤示範:使用已失效的位址,行為未定義,沒有可依賴的執行輸出。

這是不可執行的錯誤示範:local 的生命週期在函式返回時結束,p 無法合法指向可供讀取的 local。不同編譯器可能給出不同診斷及執行結果;不能依一次看到的位址、數值或終止訊號推論 C++ 的保證。

如果只要把計算結果交給呼叫端,直接回傳值最清楚,也不需要管理額外的生命週期:

直接回傳值,由呼叫端接收
#include <iostream> using namespace std; int makeNumberOK() { int value = 1234; return value; // 回傳值,不回傳區域變數的位址 } int main() { int n = makeNumberOK(); cout << "讀出來 = " << n << endl; return 0; }
預期輸出
讀出來 = 1234

區域變數 value 會在函式結束時消失,但它的值已用 return 交給呼叫端,因此 n 可安全取得 1234。只有確實需要動態生命週期時,才要另外安排所有權;newdelete 與自動管理資源的觀念見 P4

PART 07 · 多載與預設引數

同一個名字,好幾種呼叫法

C++ 允許好幾個函式共用一個名字,只要參數的個數或型別不同就行。編譯器會依照你呼叫時給的東西挑一個,這叫函式多載(function overloading)。挑選只看參數,回傳型別不算——兩個函式只有回傳型別不同的話會直接編譯錯。

同名 deliveryFee 依參數個數選擇版本;三個呼叫各自選中兩參數、一參數與無參數版本,不需要外部標頭。

int deliveryFee(); int deliveryFee(int count); int deliveryFee(int count, int rate);
同名 deliveryFee 依參數個數選擇版本;三個呼叫各自選中兩參數、一參數與無參數版本,不需要外部標頭。
#include <iostream> using namespace std; int deliveryFee() { return 12; } int deliveryFee(int count) { return count * 12; } int deliveryFee(int count, int rate) { return count * rate; } int main() { cout << deliveryFee(3, 10) << " " << deliveryFee(5) << " " << deliveryFee() << endl; return 0; }
預期輸出
30  60  12

同名 deliveryFee 依參數個數選擇版本;三個呼叫各自選中兩參數、一參數與無參數版本,不需要外部標頭。

預設引數

另一條路是給參數一個預設值,呼叫時可以省略:

省略的參數自動吃預設值
#include <iostream> #include <string> using namespace std; void report(string name, int score = 60, bool pass = true) { cout << name << " " << score << " " << (pass ? "通過" : "未通過") << endl; } int main() { report("小明", 92, true); report("小華", 55); // pass 用預設值 true report("小美"); // score 與 pass 都用預設值 return 0; }
預期輸出
小明 92 通過
小華 55 通過
小美 60 通過

三次呼叫分別給了 3、2、1 個引數。省略的部分從左到右對不上就補預設值,所以 report("小華", 55)pass 用的是 true

⚠️ 預設引數只能靠右邊排

因為呼叫端是由左往右對位的。如果 void f(int a = 1, int b) 合法,那 f(5) 到底是給 a 還是給 b?編譯器不猜,直接拒絕。

把有預設值的參數放前面,編譯器直接擋下
#include <iostream> #include <string> using namespace std; void report(int score = 60, string name) { // 有預設值的參數放前面 cout << name << " " << score << endl; } int main() { report(90, "小明"); return 0; }
預期輸出
[編譯失敗]
cell.cpp:5:36: error: default argument missing for parameter 2 of ‘void report(int, std::string)’
    5 | void report(int score = 60, string name) {   // 有預設值的參數放前面
      |                             ~~~~~~~^~~~

default argument missing for parameter 2,訊息說得很白:第 2 個參數也得有預設值。規則記成一句話:有預設值的參數必須排在最後面,而且一路排到底

💡 多載與預設引數會打架

同時寫 void f(int a)void f(int a, int b = 0),那 f(3) 兩邊都符合,編譯器會回報 call of overloaded ... is ambiguous。這兩個工具解決的是同一個問題,挑一個用就好。

PART 08 · 遞迴與呼叫堆疊

函式呼叫自己,堆疊上就多一個框

每次呼叫函式,系統會在呼叫堆疊(call stack)上開一個新的堆疊框(stack frame),裡面放這一次呼叫的參數、區域變數,以及「回去之後要從哪一行繼續」。函式回傳時這個框就被彈掉。遞迴沒有任何特別之處,它只是讓框疊得比較高。

把進出的順序印出來
#include <iostream> using namespace std; int depth = 0; int sumTo(int n) { for (int i = 0; i < depth; i++) cout << " "; cout << "進入 sumTo(" << n << ")" << endl; depth++; int result; if (n <= 1) { result = n; // 基底情形:不再往下叫 } else { result = n + sumTo(n - 1); // 遞迴情形:問題變小一號 } depth--; for (int i = 0; i < depth; i++) cout << " "; cout << "離開 sumTo(" << n << ") 回傳 " << result << endl; return result; } int main() { int answer = sumTo(4); cout << "答案 = " << answer << endl; return 0; }
預期輸出
進入 sumTo(4)
  進入 sumTo(3)
    進入 sumTo(2)
      進入 sumTo(1)
      離開 sumTo(1) 回傳 1
    離開 sumTo(2) 回傳 3
  離開 sumTo(3) 回傳 6
離開 sumTo(4) 回傳 10
答案 = 10

縮排就是堆疊的高度。注意四個「進入」全部印完,才開始印「離開」——因為 n + sumTo(n - 1) 這一行必須等右邊算完才能相加,所以 sumTo(4) 的框會一直卡在堆疊上等。這就是為什麼遞迴的空間複雜度是 O(深度)

按「開始」,看堆疊框怎麼疊上去、又怎麼收回來。
速度 堆疊上的框:0
程式碼 CODE
int sumTo(int n) { if (n <= 1) { return n; } return n + sumTo(n - 1); } cout << sumTo(4);
兩個必備零件 KEY

基底情形:小到不必再遞迴的情況,這裡是 n <= 1
遞迴情形:把問題變小一號再交給自己。
少了基底情形,或問題沒有真的變小,堆疊就會一直疊上去。

過深的遞迴會耗盡堆疊

呼叫堆疊的可用容量依作業系統、執行緒設定與編譯方式而異。即使有基底情形,過深的遞迴仍可能耗盡資源;此外也須檢查計算結果是否超過回傳型別範圍。

錯誤風險示範:過深遞迴與數值範圍
#include <iostream> using namespace std; int sumTo(int n) { if (n <= 1) return n; return n + sumTo(n - 1); } int main() { cout << "sumTo(1000) = " << sumTo(1000) << endl; cout << "sumTo(1000000) = " << sumTo(1000000) << endl; // 堆疊撐不住 return 0; }
預期輸出
風險示範,不提供固定輸出:過深遞迴可能耗盡堆疊,總和也可能使 int 溢位。

在可容納結果且堆疊足夠的環境中,sumTo(1000) 得到 500500。sumTo(1000000) 除了可能耗盡堆疊,結果 500000500000 也超出常見 32 位元 int。此例不作為可執行範本;只換較寬型別仍不能解決過深呼叫,應選擇適合的迴圈或其他做法。

💡 遞迴的框裡有什麼

每個框裝的是這一次呼叫專屬的參數與區域變數。所以 sumTo(4)nsumTo(3)n 是兩個不同的東西,互不干擾。這也回頭解釋了 PART 06:區域變數活在框裡,框一彈掉它就沒了,回傳它的位址當然拿不到東西。

EX · 隨堂練習

再一題確認觀念

EXERCISE · 這個函式錯在哪

有人寫一個函式,裡面宣告 int n=31;,然後回傳 &n。呼叫端經由這個位址讀取整數。問題是什麼?

(A) 語法錯誤,編不過
(B) 回傳了區域變數的位址,函式一結束那塊就失效了
(C) 忘了寫 delete,會記憶體洩漏
(D) 函式不該回傳指標
REFERENCE · 速查表

P3 速查表

三種傳法對照(整頁最該記住的一張表)

傳值 f(T x)傳參考 f(T& x)const 參考 f(const T& x)傳指標 f(T* p)
函式拿到什麼副本本人的別名本人的唯讀別名本人的位址
改得動呼叫端嗎不行可以不行(編譯期擋)可以
複製成本整個物件一個指標值(大小依實作)
可以是「沒有」嗎不行不行不行可以nullptr
綁定後能換目標嗎不行不行可以
呼叫端寫法f(x);f(x);f(x);f(&x);
函式裡取值xxx*p
什麼時候用小型別要就地改只讀大物件可能沒有、要換目標

語法骨架

// 宣告(原型):結尾要有分號 int square(int n); void addOne(int& n); // 可修改呼叫端的值 void show(const int& n); // 唯讀參考 int *findValue(int *p); // 指標參數與指標回傳值 // 定義:大括號內是函式本體 int square(int n) { return n * n; // 安全地回傳區域變數的值 } // 多載:參數列不同,回傳型別不參與選擇 int area(int side); int area(int width, int height); // 預設引數:有預設值的參數排在右側 void report(string name, int score = 60, bool pass = true); // 遞迴:先處理基底情形,再縮小問題 int sumTo(int n) { if (n <= 1) return n; return n + sumTo(n - 1); }

Python ↔ C++ 對照

你在 Python 怎麼做C++ 的對應差在哪
函式定義寫在檔案任何地方都能用要先宣告或先定義C++ 由上而下編譯一遍,沒見過的名字就是錯
參數一律傳物件參考T x / T& x / T* pC++ 要你在簽章上明講抄不抄
函式裡對 list 做 append,外面跟著變void f(vector<int>& v)少了 & 就完全不會變
函式裡對整數重新指派,外面不變void f(int n)行為剛好一樣,理由不同
參數預設值 score=60int score = 60C++ 規定有預設值的一律排在最後
同名函式只能有一個,後蓋前多載,同名可以好幾個C++ 靠參數型別挑
回傳一個區域 list 完全安全回傳區域變數的位址是未定義行為Python 有垃圾回收,C++ 的堆疊框說收就收
遞迴太深丟 RecursionError遞迴太深可能耗盡堆疊,具體失敗方式依實作C++ 不幫你檢查深度

五條記得住的規則

① 函式要改動呼叫端的資料,參數列一定要有 &
② 只讀的大物件用 const T&,別用傳值。
③ 類別的唯讀成員函式通常需要加 const;完整規則見 P8
④ 參數可能是「沒有」時才用指標,其餘場合優先用參考。
⑤ 永遠不要回傳區域變數的位址或參考。

延伸閱讀

資源看什麼
C++ Tutor把本頁的 swap 三個版本貼進去單步跑,看兩個函式框裡的方格與箭頭。
cppreference · Reference declaration參考的完整規則,包含它為什麼不能重新綁定。
cppreference · Default arguments預設引數的完整限制。
先備 P4 · 陣列、指標與動態記憶體下一頁。*&、堆疊與堆積、newdelete 的完整版。
本站 · 遞迴把 PART 08 的呼叫堆疊實際用起來。
QUIZ · 自我檢測

自我檢測:函式與參考 隨堂自測 · 6 題

每個選項都有解說:選錯也點開看看為什麼錯。全對之後再往下翻詞彙卡。

Q1.以下程式印出什麼?
#include <iostream>
using namespace std;

void bump(int n) { n = n + 10; }

int main() {
    int n = 5;
    bump(n);
    cout << n << endl;
    return 0;
}
Q2.跟上一題的程式只差一個符號,這次印出什麼?
#include <iostream>
using namespace std;

void bump(int &n) { n = n + 10; }

int main() {
    int n = 5;
    bump(n);
    cout << n << endl;
    return 0;
}
Q3.這段程式印出什麼?
#include <iostream>
using namespace std;

void grow(int *p) { *p = *p * 2; }

int main() {
    int v = 21;
    grow(&v);
    cout << v << endl;
    return 0;
}
Q4.這段程式印出什麼?
#include <iostream>
using namespace std;

int f(int a, int b = 2) { return a * b; }
double f(double a)      { return a + 0.5; }

int main() {
    cout << f(3) << " " << f(3, 4) << " " << f(3.0) << endl;
    return 0;
}
Q5.這個遞迴函式印出什麼?
#include <iostream>
using namespace std;

int mystery(int n) {
    if (n == 0) return 0;
    return n % 10 + mystery(n / 10);
}

int main() {
    cout << mystery(9305) << endl;
    return 0;
}
Q6.一個函式只需要讀取一個很大的 vector,不會改動它。參數該怎麼寫?
CARDS · 關鍵詞彙卡

關鍵詞彙卡:點卡片翻面

先看正面術語,心中默想定義再翻面對答案;洗牌後再過一輪,直到每張都能不假思索說出來。