語法書 / AA 競程語法書 上冊 / 第七單元 / 函式的概念與定義方式

7.1 函式的概念與定義方式

重複的程式碼是所有 bug 的根源。當你發現同樣的邏輯在多個地方出現,就應該把它抽出來寫成一個函式。

函式是一段可重複使用的程式碼,你提供輸入(參數),它返回輸出(回傳值)。就像工廠流水線上的一台機器,你丟進去物料,它吐出來成品。

函式的定義格式:

回傳的資料型態 函式名稱(參數1型態 參數1名稱, 參數2型態 參數2名稱, ...) {
    函式內容
    return 要回傳的值;
}

範例程式碼

範例 1:計算兩個整數的和

#include<iostream>
using namespace std;

// 定義函式:計算兩個整數的和
int sum(int x, int y) {
    // x 和 y 是參數,函式內可以使用它們
    return x + y;  // return 後面跟著要回傳的值
}

int main() {
    // 呼叫函式並把結果存到變數
    int a = 3, b = 5;
    int result = sum(a, b);  // result = 8
    cout << result << "\n";

    return 0;
}

執行結果:

8

範例 2:找兩個整數中較大的那個

#include<iostream>
using namespace std;

// 定義函式:找較大值
int maxVal(int x, int y) {
    if (x > y) {
        return x;
    }
    return y;
}

int main() {
    int a = 7, b = 3;
    cout << maxVal(a, b) << "\n";  // 輸出 7
    cout << maxVal(10, 15) << "\n";  // 輸出 15,可以直接傳數字

    return 0;
}

執行結果:

7
15

重要細節

函式宣告順序

如果你要在 main() 中呼叫 sum() 函式,必須在 main() 之前定義 sum()。順序很重要:

// ✓ 正確寫法:函式定義在 main 之前
int sum(int x, int y) {
    return x + y;
}
int main() {
    cout << sum(3, 5) << "\n";
    return 0;
}
// ✗ 錯誤寫法:這樣編譯會報錯,因為 main 看不到 sum
int main() {
    cout << sum(3, 5) << "\n";  // 此時還不知道 sum 是什麼
    return 0;
}
int sum(int x, int y) {
    return x + y;
}

同名函式是允許的(函式重載)

你可以有多個名稱相同的函式,只要它們的參數個數或型態不同就行。編譯器會根據你傳入的參數自動選擇合適的版本:

#include<iostream>
using namespace std;

// 版本 1:兩個 int 的 max
int max(int x, int y) {
    return (x > y) ? x : y;
}

// 版本 2:兩個 long long 的 max(參數型態不同)
long long max(long long x, long long y) {
    return (x > y) ? x : y;
}

// 版本 3:三個 int 的 max(參數個數不同)
int max(int x, int y, int z) {
    if (x > y && x > z) return x;
    if (y > z) return y;
    return z;
}

int main() {
    cout << max(3, 5) << "\n";           // 呼叫版本 1,輸出 5
    cout << max(1000000000000LL, 999999999999LL) << "\n";  // 呼叫版本 2
    cout << max(3, 5, 7) << "\n";        // 呼叫版本 3,輸出 7

    return 0;
}

函式可以呼叫函式(巢狀呼叫)

函式的參數可以是任何運算式——包括另一次函式呼叫的結果。數學課上看過的 f(f(2)),翻成程式就是 f(f(2)):先算裡面的 f(2),再把結果丟進外層的 f

#include<iostream>
using namespace std;

// 對應數學函數 f(x) = x^2 + 2x + 3
int f(int x) {
    return x * x + 2 * x + 3;
}

int main() {
    cout << f(2) << "\n";        // 11
    cout << f(f(2)) << "\n";     // 先算 f(2) = 11,再算 f(11) = 146
    cout << f(1) + f(2) << "\n"; // 函式呼叫也能參與四則運算:6 + 11 = 17

    return 0;
}

執行結果:

11
146
17

同樣地,一個函式的內容裡也可以呼叫別的函式。例如已經有找兩數較大值的 maxVal(),「三個數的最大值」就能直接疊出來,不必重寫比較邏輯:

// 函式裡面可以呼叫別的函式
int maxOfThree(int x, int y, int z) {
    return maxVal(maxVal(x, y), z);
}

maxVal(maxVal(x, y), z) 先找出 xy 中較大者,再拿它和 z 比。題目給你一條數學函數的定義(例如 f(x) = |x - 2|g(x) = f(f(x)^3 + f(x) + 1)),照著定義各寫一個函式、再照式子組合呼叫,就是最不容易出錯的做法——千萬不要試著手動把整條式子展開。

函式的排版

函式定義的排版和前面一脈相承——{ 接在行尾、函式體縮排一層;參數列則比照「逗號後空格」的習慣:

// ✓ 好的排版
int add(int a, int b) {
    return a + b;
}

// ✗ 不好的排版
int add(int a,int b){return a+b;}
規則 ✓ 好的做法 ✗ 不好的做法 為什麼
函式名接括號不空格 add(int a, int b) add (int a, int b) 函式名和 ( 緊貼,跟關鍵字 if (for ( 剛好相反
參數逗號後空格 int a, int b int a,int b 和宣告、初始化一致(1.5 提過)
左大括號接行尾 int add(...) { { 另起一行 if、迴圈同一套習慣
函式體縮排一層 return ...; 往右縮 頂著最左邊 一眼看出這些是函式內部的動作

動手試試看:修改範例 2,定義一個 minVal() 函式找最小值,然後在 main() 中呼叫它找出三個數中的最小值。