首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言選擇結構完全指南:if、switch、巢狀、最佳實踐與常見陷阱

     編輯:關於C語言

兩大選擇結構

結構適用場景關鍵字表達式型別
if-else區間判斷、複合條件、布林邏輯、指標/浮點比較if, else, else if任意標量 (整數、浮點、指標),非零為真
switch離散常數值匹配 (枚舉、字元、整數常數)、狀態機switch, case, default整數型別 (含 char, enum),編譯期常數

if 語句完整語法


// 1. 單分支
if (expr) statement;
// 或
if (expr) { block; }

// 2. 雙分支
if (expr) statement1; else statement2;
// 或
if (expr) { block1; } else { block2; }

// 3. 多重分支 (else-if 鏈)
if (expr1) { ... }
else if (expr2) { ... }
else if (expr3) { ... }
...
else { ... }  // 兜底

關鍵規則

  • 無大括號時:if 僅管制**緊接的單一陳述式** (含空語句 ;),後續程式碼順序執行。
  • 有大括號:形成**複合陳述式**,管制區塊內所有陳述式。
  • 懸空 else:else 總與**最近的未配對 if** 綁定 (就近原則)。用大括號消除歧義。
  • 條件運算子優先級:== > && > || > ?: > =,複雜條件加括號。

經典陷阱

❌ 陷阱 1:誤用 = 而非 ==


if (x = 5) { ... }  // 永遠為真,x 被指派 5
// 編譯器警告: -Wparentheses / -Wassignment-in-condition

✅ 防禦:常數左側 if (5 == x) (C 風格) 或啟用警告視為錯誤

❌ 陷阱 2:浮點數直接比較相等


double d = 0.1 + 0.2;
if (d == 0.3) ...  // 可能為假 (0.30000000000000004)

✅ 正確:epsilon 比較 fabs(a-b) < 1e-9

❌ 陷阱 3:懸空 else


if (a)
    if (b) foo();
else bar();  // else 配對給 if(b),非 if(a)!

✅ 修正:加大括號


if (a) {
    if (b) foo();
} else bar();

switch 語句完整語法


switch (expr) {
    case const1: statements1; break;  // break 結束 switch
    case const2: statements2; break;
    case const3:
    case const4: statements34; break; // 多標籤共用程式碼
    default: default_stmts; break;    // 兜底 (可選)
}

關鍵規則

  • expr 必須為**整數型別** (int, char, short, long, enum);不可為浮點、字串、結構體。
  • case 標籤必須為**編譯期整數常數表達式**;不可為變數、函數呼叫。
  • 貫穿:無 break 時,執行完當前 case 繼續執行下一 case (C 語言預設行為,常用於合併處理)。
  • default 位置任意 (慣例放最後);若無 default 且無匹配 case,整個 switch 無動作。
  • switch 內可宣告變數 (C99),但**初始化會被跳過** (跳躍過初始化) → 需用區塊 { int x = 0; } 包裹。

編譯器最佳化:跳躍表 vs 二分搜尋

  • case 值連續/稠密 → 編譯器生成 **跳躍表** (O(1) 間接跳躍)。
  • case 值稀疏 → 生成 **二分搜尋決策樹** (O(log n))。
  • 效能通常優於等價 if-else 鏈。

巢狀選擇結構


// if 巢狀 switch
if (user_logged_in) {
    switch (user_role) {
        case ADMIN:   ShowAdminPanel(); break;
        case EDITOR:  ShowEditorPanel(); break;
        case VIEWER:  ShowViewerPanel(); break;
        default:      ShowGuestPanel();
    }
} else {
    ShowLoginPage();
}

// switch 巢狀 if
switch (event_type) {
    case KEY_PRESS:
        if (key == 'q') Quit();
        else if (key == 'h') Help();
        break;
    case MOUSE_CLICK:
        if (btn == LEFT) Select();
        else if (btn == RIGHT) ContextMenu();
        break;
}

現代最佳實踐

1. 衛語句 — 扁平化條件


// ❌ 巢狀地獄
void Process(User *u) {
    if (u) {
        if (u->active) {
            if (u->permission >= READ) {
                DoWork(u);
            }
        }
    }
}

// ✅ 衛語句
void Process(User *u) {
    if (!u) return;
    if (!u->active) return;
    if (u->permission < READ) return;
    DoWork(u);
}

2. 查表法替代巨大 switch/if-else


typedef void (*Handler)(void);
Handler handlers[256] = {0};

void Init() {
    handlers['a'] = HandleA;
    handlers['b'] = HandleB;
    // ...
}

void Dispatch(char cmd) {
    if (handlers[(unsigned char)cmd]) handlers[(unsigned char)cmd]();
    else DefaultHandler();
}

3. 狀態機模式 (嵌入式常用)


typedef enum { IDLE, RUNNING, PAUSED, ERROR } State;
State current = IDLE;

void EventHandler(Event e) {
    switch (current) {
        case IDLE:
            if (e == START) current = RUNNING;
            break;
        case RUNNING:
            if (e == PAUSE) current = PAUSED;
            else if (e == STOP) current = IDLE;
            else if (e == FAULT) current = ERROR;
            break;
        // ...
    }
}

4. C23 switch 擴充 (提案) — 模式匹配

未來可能支援:switch (x) { case 1..10: ...; case >100: ...; } 範圍匹配。

效能微基準 (10^8 次迴圈, -O2)

結構情境相對耗時
switch (連續 case)跳躍表1.0x (基準)
switch (稀疏 case)二分搜尋1.3x
if-else 鏈 (命中首項)分支預測友善1.1x
if-else 鏈 (命中尾項)多次分支錯誤2.5x
查表法 (函數指標陣列)間接呼叫1.2x

結論:**熱路徑優先用 switch (連續值) 或查表法**;冷路徑/複雜條件用 if-else + 衛語句。

結語

選擇結構是程式流程控制的骨架。精通 if-else 的布林邏輯與衛語句扁平化、switch 的跳躍表最佳化與貫穿語意、巢狀結構的狀態機建模、查表法替代巨大條件鏈,能寫出可讀、可維護、效能可預測的 C 程式碼。下次寫條件判斷時,先問:值是否為離散常數?是否為熱路徑?能否提前返回? — 選擇最適工具。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved