語法書 / AA 競程語法書 下冊 / 附錄 / G. switch 與 fall-through

G. switch 與 fall-through

switch 是多路分岔的另一種寫法:拿一個整數值去對號入座,跳到對上的那個入口,然後一路往下執行。最後那六個字就是整節的重點,也是考卷最愛設計的陷阱——程式識讀考古題很常出現 switch,考的正是這件事。

語法:跳到入口,然後往下跑

switch (op) {          // 括號裡放一個整數值
    case 1:            // 入口:op 等於 1 就從這裡開始執行
        cout << "add";
        break;         // 跳出整個 switch
    case 2:
        cout << "sub";
        break;
    default:           // 沒有任何 case 對上時的入口
        cout << "unknown";
}

換成上冊 3.5if / else if,行為完全一樣:

if (op == 1) cout << "add";
else if (op == 2) cout << "sub";
else cout << "unknown";

兩者的差別逐格對照如下:

if / else if switch
每個分支怎麼判斷 各寫各的條件式,彼此可以毫無關係 一律拿括號裡那個值跟 case 後面的常數比相等
括號裡能放什麼 任何條件式(比較、邏輯運算、整數都行) 只能是整數類的值(intchar
選中之後 做完那一段,整組結束 從入口往下一路做,直到 break 或右大括號

前兩列是限制,最後一列才是真正的行為差異——它有個名字,叫 fall-through。

case 後面只能是一個常數

case 後面的值必須在編譯時就算得出來,放變數是編譯錯誤:case n: 得到的是 the value of ‘n’ is not usable in a constant expression,寫成 case x > 2: 也一樣,編譯器抱怨的同樣是 x 不是常數。標準的 C 與 C++ 也沒有「一個 case 涵蓋一段範圍」的寫法,要涵蓋多個值只能一個一個列出來。

被判斷的值同樣有限制:switch (x)x 必須是整數類,換成 double 或字串一律 switch quantity not an integerchar 可以——它本來就是整數(8.1)。

break 與 fall-through

break 在這裡的意思是跳出整個 switch(跟上冊 4.5 跳出迴圈是同一個關鍵字,但跳出的對象不一樣)。少了它會怎樣?

#include <iostream>
using namespace std;

void test(int x) {
    cout << "x = " << x << ":";
    switch (x) {
        case 1:
            cout << " one";
            // fall through - 沒有 break,接著執行 case 2
        case 2:
            cout << " two";
            break;
        case 3:
            cout << " three";
            // fall through - 沒有 break,接著執行 default
        default:
            cout << " other";
    }
    cout << '\n';
}

int main() {
    for (int i = 1; i <= 4; i++) {
        test(i);
    }
    return 0;
}

執行結果:

x = 1: one two
x = 2: two
x = 3: three other
x = 4: other

逐行看:x1 時從 case 1 進場,印完 one 沒遇到 break,於是繼續往下印 two,直到那裡的 break 才離開;x2 只印 two;x3 印完 three 之後掉進 default,多印一個 other;x4 沒有任何 case 對上,從 default 進場。

所以 case 不是四個各自獨立的盒子,而是四個入口。考題會刻意在某幾個 case 後面不寫 break,你要做的就是找到入口,然後老老實實往下讀到 break 或右大括號為止。

default 習慣寫在最後,但它擺在任何位置都可以——它只負責「沒有 case 對上時從我開始」,不影響哪個入口被選中。

刻意的 fall-through:多個 case 共用一段

fall-through 不全是陷阱,它有一個正經用途:好幾個值要做同一件事。把空的 case 一個接一個疊起來,就成了「或」:

switch (s[i]) {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
        cnt++;         // 五個入口共用這一段
        break;
}

這段等價於 if (s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u') cnt++;。看到一整排空的 case,直接讀成一個 || 就對了。