[C++ 入門] 2. 程序流程:if-else 條件判斷 & switch-case 分支結構

先前我們的程式很單純,就是順順得執行下去。再來的幾篇文章要介紹的陳述能讓我們的程式多些變化,並解決更多問題。

if-else 條件判斷

還記得1.1 的範例 嗎??我們寫惹個程式,允許使用者輸入兩個整數,而我們輸出兩者相除之商與餘數。然而,如果今天有人很 87 輸入87 0, 那麼會發生什麼事??

電腦會爆炸嗎??顯然不會。如果你在 VS Code 按下 F5 偵錯,這時程式回停下來告訴你 Exception has occurred. Arithmetic exception. 如果直接在終端機執行,會輸出 floating point exception 之類的。總之,程式不正常得提前結束惹。

這邊我們稍微補充一下,平常我們 main() 函式都 return 0; 代表正常結束,但這次會回傳 136, 確認的方法是在終端機輸入 echo $?. 原因是當我們的程式發生例外而不處理時,會觸發若干 Signal, 像是執行到一半按下 ⌃Ctrl + C 會觸發 SIGINT (2) 使程式中斷、程式被 abort (比如 assert 沒過)會觸發 SIGABRT (6), 錯誤地存取記憶體會觸發 SIGSEGV (11), 而本例致命的算術運算錯誤觸發 SIGFPE (8). 這些都是日後我們常遇到的錯誤。發生例外回傳值就不為 0, 而會是 128 + (n). 其它的 exit code 包括 126 權限不足、127 command not found, …

回過頭來我們的問題。在設計程式時,必須盡可能考慮一切狀況,畢竟使用者是沒有下限地 (? 因此,我們需要檢查除數不可為零。if statement 派上用場惹。首先看看它的語法:

if (condition)
{
    // ...
}

其中 condition 是個表達式,如果表達式非零,就會執行大括弧的內容,否則直接跳過。因此我們可以改進我們的除法計算器: 如果大括弧內只有一個 statement (1 semicolon) 可以偷懶省略。其實在 C/C++ 中,非零就是 true, 因此也能寫成 if (b) cout << ..., 就是省略 != 0 的部分。現在再執行一次,故意輸入 87 0, 程式不再發生錯誤,但直接結束好像有點怪怪的怪怪的欸??所以我們可以應用 else:

if (condition)
{
    // ...
}
else
{
    // ...
}

如果 condition 非零,就會執行 if 大括弧的內容,否則執行 else 大括弧的內容,最後繼續執行下去。我們再次改進我們的除法計算器: 這次再有 87 輸入 87 0, 就會告訴他除數不可為零惹。當然我們的 condition 不見得只是一個關係運算式,還可以結合前一篇介紹的邏輯運算子等等。我們可以一樣在第十行前方點一下設置中斷點,這次不要急著按 F5 繼續執行,改按 F10 Step Over 逐步執行,看看下一行會跳至何處。

類題演練

多重與巢狀 if-else

你以為 if-else 就這樣??不不不。我們的 if-else 還可以跟 if-else 自己結合,當然也可以跟下篇介紹的迴圈結合。自己結合的方式有並排的也有巢狀的:

if (condition_0)
{
    // ...
}
else if (condition_1)
{
    // ...
}
else
{
    // ..
}
if (condition_0)
{
    if (condition_1)
    {
        // ...
    }
    else
    {
        // ...
    }
}
else
{
    if (condition_2)
    {
        // ...
    }
    else
    {
        // ...
    }
}

這邊應該都還很好理解,就給各位自行體驗惹。做些例題可以確認自己是否真的了解惹。

範例解析

  • GreenJudge c032
    • 大家應該多少有聽過學年學分制,本題輸入兩學期成績,要求計算所得學分數。簡單的想法是先計算兩學期平均是否大於六十,再判斷各自是否大於六十,但注意的整數的除法,所以我們可以移項。
    • 另解甚至根本不用 if-else, 歡迎細細品味
  • GreenJudge c014
    • 這題最單純的想法就是乖乖照題目的條件列式,注意 b 有可能小於 a
    • 這邊同樣提供不用 if-else 的另解

類題演練

switch-case 分支結構

switch-case 感覺比較少用,但還是介紹一下。舉例來說高三上開始一週補習四天,每次想晚餐吃什麼這個大哉問實在太痛苦惹,於是我們決定週二吃 SUKIYA、週三吃那個那個飯、週四吃癡麵、週日吃 Burger King. 現在寫個程式,輸入一個整數代表是週幾,其中 0, 7 皆表示週日,輸出晚餐要吃什麼。

我們現在會 if-else, 當然可以寫出以下程式:

#include <iostream>

using namespace std;

int main()
{
    int day;
    cin >> day;
    if (day == 2)
        cout << "SUKIYA\n";
    else if (day == 3)
        cout << "那個那個飯\n";
    else if (day == 4)
        cout << "癡麵\n";
    else if (day == 0 || day == 7)
        cout << "Burger King\n";
    else
        cout << "No cram school today.\n";
    return 0;
}

但這樣有點囉嗦,是吧。所以我們可以用 switch-case 改寫之: 程式碼的可讀性好惹一些,只是注意每個 case 結束後如果沒有 break; 預設會進入下個 case. 另外,C/C++ 的 switch-case 僅支援整數包含字元。乍看之下用 switch-case 程式碼似乎比較冗長,但理論上執行時,if-else 在最糟情況下會遍歷 \(n\) 個判斷,時間複雜度是 \(O(n)\) 線性的,但 switch-case 編譯時可能使用 jump table, lookup table; 達到 \(O(\log n)\) 對數的甚至 \(O(1)\) 常數的時間複雜度。當然大部分情況下差異微乎其微,只是 switch-case 還有許多妙用。

類題演練

另外 gcc 有提供 case range 可以這樣用:

switch (n)
{
// ...
case 69 ... 87:
    cout << "skr skr\n";
    break;
// ...
}

__int128_t 一樣,macOS 如果用蘋果的 clang 應該無法使用。


comments powered by Disqus