1 if/if-else 基礎與分支流程判斷
if 就像十字路口的紅綠燈:程式跑到這裡,會先檢查條件式的真假——條件成立(非0,也就是「真」)就走進 if 那個路口;不成立(0,也就是「假」)就直接跳過,改走 else 那條路(若沒寫 else,不成立時就什麼都不做,繼續往下一行執行)。
| 語法形式 | 行為 | 白話比喻 |
|---|---|---|
if(條件) A; |
條件為真才執行 A,為假就直接跳過整個 if,不做任何事。 | 紅燈才停車,綠燈直接通過,路口沒有第二個選擇。 |
if(條件) A; else B; |
條件為真執行 A;為假執行 B。兩者必擇一,不會都做也不會都不做。 | 路口只有兩條路,紅燈往左,綠燈往右,一定會走其中一條。 |
#include <stdio.h>
int main(void) {
int temperature = 28;
if (temperature >= 30) {
printf("好熱,開冷氣!\n");
} else {
printf("還算舒適。\n");
}
return 0;
}
==」誤寫成「賦值 =」
if (flag = 0) 是合法的 C 語法,但它的意思是「把 0 賦值給 flag,再拿賦值後的結果(0,也就是假)當作條件」——並不是在比較 flag 是否等於 0!
這種寫法幾乎每次都是手誤,因為它會讓 if 恆為某個固定值,完全喪失「依變數當下狀態做判斷」的意義。統測常用這個陷阱測驗學生是否真正理解「賦值」與「比較」是兩回事。
2 if-else if 鏈狀結構與邊界值陷阱
當條件不只兩種,而是要分成好幾個等級時,就要用 if-else if-else 鏈狀結構。它的執行方式像「瀑布式關卡」:程式由上往下逐一檢查,只要遇到第一個成立的條件,就執行對應的程式碼並直接跳出整條鏈,後面的條件完全不會再被檢查,即使它們理論上也成立。
想像成績分級系統像一連串的瀑布關卡:先問「有沒有超過90?」——過了就直接掉進「A級」的水潭,不會再往下掉去檢查「有沒有超過80」。只有沒過90的人,才會繼續往下一關「有沒有超過80?」检查。順序很重要:條件的撰寫順序若顛倒,會讓某些等級永遠不可能被判斷到。
#include <stdio.h>
int main(void) {
int score = 60;
if (score >= 90) printf("A\n");
else if (score >= 80) printf("B\n");
else if (score >= 60) printf("C\n"); // 60剛好落在這一段
else printf("F\n");
return 0;
}
>= 還是 >?
score >= 60(大於等於,包含60分本身)跟 score > 60(嚴格大於,不含60分)在「剛好等於邊界值」時會產生完全不同的結果!統測經常把邊界值設計成剛好卡在條件上,就是要看你有沒有注意到 >= 與 > 只差一個等號、行為卻天差地遠。做這類題目時,請務必把「邊界值本身」代入程式碼手動推演一次。
| 寫法 | score = 60 時是否成立 | score = 59 時是否成立 |
|---|---|---|
score >= 60 | ✅ 成立(60也算) | ❌ 不成立 |
score > 60 | ❌ 不成立(60不算,要嚴格大於) | ❌ 不成立 |
3 switch-case 語法結構與執行流程
當同一個變數要對應「很多種固定數值」的分支時,一長串 if-else if 會顯得又臭又長;這時可以改用 switch-case,它就像捷運站的月台指標——依照手中車票(switch的判斷值)上寫的號碼,直接跳到對應的月台(case),不需要像 if-else if 那樣一關一關檢查過去。
| 比較項目 | if-else if 鏈 | switch-case |
|---|---|---|
| 適合場景 | 條件是「範圍區間」(如 score >= 60)或多個不同變數的邏輯組合 |
條件是「單一變數」對應「多個固定離散值」(如 day == 1, 2, 3...) |
| 判斷方式 | 由上而下逐條檢查,第一個成立就執行並結束 | 直接依數值或字元跳到對應 case 進入點 |
| 結束機制 | 條件成立後自動結束整條鏈 | 不會自動結束,必須明確寫 break; 才會跳出 |
#include <stdio.h>
int main(void) {
int day = 3;
switch (day) {
case 1: case 2: case 3: case 4: case 5:
printf("平日\n");
break;
case 6: case 7:
printf("假日\n");
break;
default:
printf("不合法的輸入\n");
}
return 0;
}
1. switch() 括號內的判斷值只能是整數或字元(int、char、enum 等),不能是浮點數或字串。
2. 每個 case 常數值: 後面接的是「進入點」,不是條件式,常數值必須是編譯時期已知的固定值。
3. default: 是「以上皆非」的收尾分支,可放在任何位置(雖然習慣上放最後),沒有比對到任何 case 時就會執行它。
4 switch 貫穿(Fall-through)陷阱全解析 — 統測最愛的坑
這是本章統測命中率最高的觀念!C 語言的 switch 一旦進入某個 case,就會像忘記剎車的溜滑梯一樣,一路往下貫穿執行後面所有的敘述,直到遇到 break; 或整個 switch 區塊結束為止——完全不會理會後面 case 標籤寫的是什麼數字,因為那些數字在貫穿時只是「路過的地標」,不會被重新拿來比對判斷值。
#include <stdio.h>
int main(void) {
int i = 2;
switch (i) {
case 1: printf("A"); break;
case 2: printf("B"); // 沒有break!
case 3: printf("C"); break; // 會被貫穿執行
default: printf("D");
}
return 0;
}
1. i = 2,switch 直接跳進 case 2: 這個進入點,印出 B。
2. case 2 這一行的結尾沒有 break;,所以程式並不會離開 switch,而是直接往下一行執行,剛好下一行是 case 3: 底下的程式碼,於是印出 C。
3. case 3 這次有寫 break;,程式才真正跳出 switch,default 完全不會執行到。
最終輸出為 "BC"。這正是「貫穿」——不是 bug,是 C 語言 switch 天生的行為,寫程式時若不想要這個效果,就必須每個 case 都手動加上 break。
「沒 break 不停,一路溜到底」——只要沒看到 break;,就假設程式會一直往下執行,不管中間路過幾個 case 標籤,直到真的遇到 break 或整個 switch 的右大括號 } 為止。
5 多重 case 共用程式碼技巧(有意貫穿)
貫穿並非總是「忘記寫 break」的錯誤,它也可以是刻意設計的技巧:當好幾個不同的 case 值都要執行「完全相同」的一段程式碼時,可以把這些 case 標籤緊鄰疊在一起、共用最後那一段程式碼,就像好幾個不同門牌號碼共用同一個信箱一樣。
#include <stdio.h>
int main(void) {
int day = 3;
switch (day) {
case 1: case 2: case 3: case 4: case 5:
printf("平日\n");
break;
case 6: case 7:
printf("假日\n");
break;
default:
printf("不合法的輸入\n");
}
return 0;
}
case 1: case 2: case 3: case 4: case 5: 這五行中間都沒有任何程式碼或 break,所以無論 day 是 1、2、3、4 還是 5,都會一路貫穿到最後才共同執行 printf("平日\n"); break;。這是「有意貫穿」——利用貫穿特性省去重複撰寫五次一模一樣的程式碼。
關鍵在於兩個 case 之間有沒有夾著程式碼:
• 如果兩個 case 標籤緊鄰、中間完全沒有程式碼(如 case 1: case 2:),這是刻意設計的「分組」,讓多個值共用同一段邏輯。
• 如果 case 標籤中間夾著一段 printf 或其他敘述、卻沒接 break(如本章第4節的 case 2: printf("B"); 沒接 break 就換行到 case 3),這通常代表「忘記寫 break」,會造成非預期的貫穿。統測最愛在這種地方出題考細心程度!
6 enum 列舉型態:自動編號與指定初值規則
enum(列舉)可以把一組有意義的具名常數包裝成一個新型態,讓程式碼用 Run、Stop 這種有意義的名字取代難以理解的數字 1、2、3。它的編號規則就像停車場的樓層自動編號機:預設從 0 開始,每往下一個成員就自動 +1;如果中途有人手動指定了某個樓層數字,編號機就會從那個數字繼續往下遞增。
| enum 成員 | 規則 | 範例值 |
|---|---|---|
| 沒有指定初值的第一個成員 | 預設從 0 開始 | enum{A}; → A=0 |
| 沒有指定初值的後續成員 | 等於「前一個成員的值 + 1」 | enum{A,B}; → A=0, B=1 |
| 有明確指定初值的成員 | 直接採用指定的數字,不受前面成員影響 | enum{A,B=10}; → A=0, B=10 |
| 指定初值之後、又沒指定的成員 | 接續「剛剛指定的那個值 + 1」繼續遞增 | enum{A,B=10,C}; → A=0, B=10, C=11 |
enum State { Run = 1, Stop, Pause = 3, Exit };
// Run=1(明確指定)
// Stop=2(接續Run+1,自動遞增)
// Pause=3(明確指定,重新設定)
// Exit=4(接續Pause+1,自動遞增)
int main(void) {
enum State current = Stop;
printf("%d\n", current); // 輸出 2
return 0;
}
很多人誤以為 Pause = 3 之後的 Exit(沒指定值)會「重新從0開始編號」,但其實規則永遠是「沿用前一個成員的值 + 1」,不管前一個成員是自動遞增出來的,還是被明確指定的。所以 Exit 一定是 Pause + 1 = 4,不會是別的數字。
7 條件判斷邏輯除錯:!flag 與比較方向陷阱
統測很喜歡出「這段程式碼邏輯寫反了,請問怎麼修改」這類除錯題。最常見的手法是先用一個 flag(布林旗標)變數記錄某個條件的真假,再用 !flag(邏輯 NOT,反相)去判斷分支——如果條件判斷式的比較方向從一開始就寫反了,後面即使 !flag 的用法完全正確,結果還是會整個顛倒,就像接反的電燈開關:開關本身沒壞,但因為線路接反,「開」反而變成「關」。
int flag = -1;
int x = 17; // x 表示完成的題目數
int y = 18; // y 表示完成的題目數
flag = (int)(x + y < 32);
if (!flag)
printf("Go! 繼續努力!\n");
else
printf("Ya! 快完成了!\n");
// 需求:總和少於32要顯示「Go!」,否則顯示「Ya!」
1. x + y = 35,實際上已經達標(≥32),理應顯示「Ya! 快完成了!」。
2. 但程式算出 flag = (35 < 32) = 0,接著 !flag = !0 = 1(真),反而執行了 if 分支印出「Go!」——結果完全顛倒!
3. 問題出在 flag 一開始被設計成「還沒達標」的旗標(x+y < 32),但後面卻用 !flag 判斷「已完成」,語意對不上。
4. 正確修法:把比較方向反過來,讓 flag = (x+y >= 32) 直接代表「已達標」,這樣 !flag 才能正確對應「還沒達標,要顯示Go!」的原始需求。
遇到這類題目,不要急著檢查 !、==、&& 這些運算子寫得對不對,先問自己:「這個 flag 變數,設計者原本想讓它代表什麼意思?」再檢查後面所有用到它的地方,是否都跟這個語意一致。本題的 bug 不在 !flag 這個用法本身(用法完全正確),而在比較方向——這是統測除錯題最容易忽略的盲點。
8 111~115 歷屆統測真題全選項深度剖析(共 5 題)
以下收錄統測專業科目(二)近年全部 5 題選擇結構(if/switch/enum)真題。每題均提供高解析度原始考題掃描圖、互動選項按鈕與所有選項逐項深入解析:
int y, a=45;
if(a>=60)
y=a+1;
else if(a>=50)
y=a+2;
else
y=a+3;
🔍 四個選項逐項深度剖析
y=a+3;,y 絕不會維持在 45 沒被賦值。y=a+1,也就是誤判 a>=60 這個條件成立了。但 a=45,45 並沒有大於等於 60,第一個 if 條件應為假,不會進入這個分支。y=a+2,也就是誤判 a>=50 這個條件成立了。但 a=45,45 同樣沒有大於等於 50,第二個 else if 條件也應為假,容易被「45很接近50」的直覺誤導而選錯。a>=60 → 45>=60 為假;第二個條件 a>=50 → 45>=50 也為假;兩者皆不成立,程式落入最後的裸 else 分支,執行 y=a+3=45+3=48。故選 (D)。#include <stdio.h>
int main(){
unsigned char i=3;
switch ( (i&0x0e) % 5){
case(1):
printf("%c", '0'+i);
break;
case(2):
printf("%c", '0'+i*i);
case(3):
printf("%c", 'a'+i*i);
default:
printf("%c", 'z');
}
return(0);
}
🔍 四個選項逐項深度剖析
00000011。i & 0x0e(0x0e=00001110)逐位元 AND 得 00000010=2;2 % 5 = 2,switch 跳進 case(2):印出 '0'+i*i = '0'+9 = '9'(ASCII加法,'0'是48,48+9=57='9')。case(2) 結尾沒有 break,貫穿進入 case(3):印出 'a'+i*i = 'a'+9 = 'j'('a'是97,97+9=106='j')。case(3) 同樣沒有 break,再貫穿進入 default:印出 'z'。三段合計輸出 “9jz”,選 (A)。printf 的 ASCII 換算過程算錯或重複套用,多印出了額外的數字字元。核心觀念沒抓穩:貫穿只是「照抄後面的程式碼繼續執行」,不會重新計算或疊加任何中間值。%c 格式化語意不熟——%c 是把運算結果的整數值當作 ASCII 碼、對照輸出對應的「字元」,而不是直接把運算後的整數值印出來。case(2) 結尾完全沒有寫 break;,程式一定會貫穿到 case(3)、default 繼續執行,並不會只印出 case(2) 的內容「9」就結束。enum States { Run = 1, Stop, Pause = 3, Exit };
States StateMachine = Pause;
int Command = 0;
std::cin >> Command;
if (Command == 1) StateMachine = Stop;
if (Command == 2 && StateMachine == Pause)
StateMachine = Run;
if (Command == 3 || StateMachine == Stop)
StateMachine = Exit;
🔍 四個選項逐項深度剖析
Command==1 為真 → StateMachine 變為 Stop(2)。第二個 if:Command==2 為假 → 跳過。第三個 if:Command==3 為假,但 StateMachine==Stop(此刻已是2)為真,|| 只要有一邊真就成立 → StateMachine 變為 Exit(4)。最終為 Exit,選 (A)。Command==2 && StateMachine==Pause 兩邊皆為真(StateMachine 此時仍是初始值 Pause),條件成立,會確實執行 StateMachine = Run;。此選項誤以為 if 只是「檢查」條件是否成立、卻忽略了 if 本體真的會執行賦值敘述,把 StateMachine 從 Pause 改成了 Run,因此最終值應為 Run(1) 而非 Pause(3)。Command==3 || StateMachine==Stop 中,Command==3 本身就已為真,依 || 邏輯或的短路特性,整個條件即成立,執行 StateMachine = Exit;。此選項很可能來自把條件式中的比較運算子 == 誤讀成賦值運算子 =,誤以為 StateMachine==Stop 這段是「把 StateMachine 設為 Stop」;但它其實只是比較,真正造成改變的是 if 外層的 StateMachine = Exit;,最終應為 Exit 而非 Stop。Command==1/2/3 全部為假,且 StateMachine 從未被改變過(仍是初始值 Pause,不等於 Stop),第三個 if 的 StateMachine==Stop 也為假,三個條件全部不成立。此選項誤以為當 Command 不符合任何專屬條件時,StateMachine 會被重置為某個預設值 Run,但事實上三個 if 都沒有成立,StateMachine 應該維持一開始的初值 Pause,並不會自動變成 Run。int flag = -1;
int x = 17; // x 表示小明完成的題目數
int y = 18; // y 表示小玲完成的題目數
flag = (int)(x+y < 32);
if (!flag)
printf("「Go!繼續努力!」");
else
printf("「Ya!快完成了!」");
🔍 四個選項逐項深度剖析
x+y < 32 這個關係運算式的比較方向與真假結果——不論是整數或浮點數,35 都不小於 32,比較結果依然相同。這個選項誤以為問題出在資料型態的精度或轉型上,卻沒發現真正的問題是條件判斷的方向從一開始就寫反了。if(!flag) 改成 if(flag = 32) 會把「邏輯判斷」偷換成「賦值運算式」:flag = 32 這個運算式的值恆為 32(非0,也就是恆為真),會讓 if 分支永遠被執行,不管 x+y 的總和究竟是多少,都只會印出「Go!」。這正是混淆「賦值運算子 =」與「邏輯判斷」差異的典型錯誤,完全無法依總和正確反映對應訊息。flag=(35<32)=0,!flag=!0=1(真)反而執行了 if 分支印出「Go!」,邏輯完全顛倒。把第6行改成 x+y >= 32 後,flag 直接代表「總和已達標」:達標時 flag=1、!flag=0 走 else 印出「Ya!」;未達標時 flag=0、!flag=1 走 if 印出「Go!」,兩種情況都正確對應需求,選 (C)。flag 的初始值 -1 在第4行就已經被 flag = (int)(x+y < 32); 這行運算結果重新覆蓋,在後續的 if 判斷中根本用不到初始值。這個選項誤以為 flag 的「初始值」在後續判斷中仍有作用,忽略了它早就被新的運算結果取代,改成 1 對最終輸出結果完全沒有任何影響。char String[16]="A1(2ABC*.BAA3CB";
int i=0, c_A=0, c_B=0, c_C=0, c_digi=0, c_sym=0;
while(String[i] != '\0'){
switch(String[i]){
case 'A':
c_A++; break;
case 'B':
c_B++; break;
case '1': case '2': case '3':
c_digi++; break;
default:
c_sym++;
}
i++;
}
printf("%d,%d,%d,%d,%d",c_A,c_B,c_C,c_digi,c_sym);
🔍 四個選項逐項深度剖析
case 'C': 分支,因而把字串中的 2 個 'C' 算進了 c_C,卻沒發現這 2 個 'C' 因為找不到任何 case 相符(程式碼完全沒有 case 'C':),實際上會落入 default,應該被計入 c_sym 而不是 c_C。case 'C':,所以 c_C 恆為 0;剩下落入 default 的字元包含真正的符號「(、*、.」共3個,加上沒有對應 case 的 2 個 'C' 字元,c_sym = 3+2 = 5。最終輸出 4,3,0,3,5,選 (B)。9 高職段考與模擬精選實戰題(5 題全選項解析)
透過以下 5 題精選模擬試題,全面檢驗你對邊界值判斷、switch 貫穿、enum 編號規則與邏輯除錯的掌握度:
int score = 70, grade;
if (score > 90) grade = 5;
else if (score > 80) grade = 4;
else if (score > 70) grade = 3;
else if (score > 60) grade = 2;
else grade = 1;
🔍 四個選項逐項深度剖析
score > 90(70>90)明顯為假,不會進入這個分支。score > 80 成立,但 70 並不大於 80,這個條件應為假。容易發生在快速瀏覽數字、沒有真的把 70 代入每一行比較運算的情況。score > 70 成立,但這裡用的是嚴格大於,70 並不大於 70 本身(70>70 為假)!這正是把 > 誤看成 >= 的典型邊界值陷阱——70 剛好卡在邊界上,若忽略「嚴格大於」這個細節,就會誤以為條件成立。70>90 假;70>80 假;70>70 假(70不嚴格大於70);70>60 為真 → 進入這個分支,grade=2。故選 (D)。int score = 75, grade = 0;
switch (score / 10) {
case 10:
case 9: grade = 1; break;
case 8: grade = 2; break;
case 7:
case 6: grade = 3; break;
default: grade = 4;
}
printf("%d", grade);
🔍 四個選項逐項深度剖析
score/10 為整數除法,75/10=7(捨去小數)。switch 跳進 case 7:,這一行中間沒有任何程式碼與 break,直接貫穿到 case 6: 底下的 grade=3; break;,執行後 grade=3,印出 3,選 (C)。case 7: 沒有直接接程式碼),因而預期會落入 default。但其實 case 7: 透過「貫穿」與 case 6: 共用了同一段程式碼,並不會跳到 default——只要 case 標籤本身數值相符,就一定會從那個進入點開始執行,不管它自己底下有沒有直接接程式碼。enum Level { Bronze, Silver = 5, Gold, Platinum = 10, Diamond };
int total = Gold + Diamond;
printf("%d", total);
🔍 四個選項逐項深度剖析
Silver=5 之後,所以 Gold=5+1=6,並不是脫離前文、重新從0算起的1。Platinum = 10 已經明確重新指定了初值。Diamond 其實是接在 Platinum 之後,等於 Platinum+1=11,而不是無視 Platinum、直接沿用 Gold 繼續累加。total = Gold + Diamond = 6 + 11 = 17,選 (C)。int flag = 0;
if (flag = 5) {
printf("A");
} else {
printf("B");
}
printf("\nflag = %d", flag);
🔍 四個選項逐項深度剖析
flag = 5 是完全合法的 C 語言運算式:它會把 5 賦值給 flag,而且整個賦值運算式本身的「值」就等於 5(非0,也就是真)。因此 if(flag = 5) 條件成立,執行 if 分支印出 A;同時這個賦值也是真的執行過的敘述,flag 最終確實變成 5。if(flag = 5) 看成是在「比較」flag 是否等於 5(誤讀成 flag == 5)。因為 flag 原本是 0,若真的用 == 比較,0==5 為假,才會誤判跳到 else 印出 B。但 C 語言允許把「賦值運算式」直接當作 if 的條件式,這裡是先賦值、再拿賦值後的結果做真假判斷,並不是比較。flag = 5 這個賦值敘述本身就會確實地把 flag 的值永久改成 5,並不是條件判斷結束後就恢復原狀,程式最後印出的 flag 值就是 5。#include <stdio.h>
int main(void) {
int n = 2;
switch (n) {
case 1:
printf("one");
case 2:
printf("two");
case 3:
printf("three");
break;
case 4:
printf("four");
default:
printf("other");
}
return 0;
}
🔍 四個選項逐項深度剖析
case 2: 這一行結尾完全沒有寫 break;,程式會繼續往下貫穿執行,並不會只印出「two」就停止。case 2:,印出 “two”。這一行結尾沒有 break,貫穿進入 case 3:,印出 “three”;case 3 這裡有寫 break;,程式立刻跳出整個 switch,case 4 與 default 完全不會執行。最終輸出 “twothree”,選 (B)。break; 不會生效、或不小心看漏了它的存在,以為程式會繼續貫穿到 case 4。但 case 3 結尾確實寫了 break,程式在印出 "three" 之後就會真正跳出 switch,不會再執行 case 4 的程式碼。10 考前衝刺 10 秒速記口訣 & 核心知識檢核清單
第一個成立的條件就執行並跳出整條鏈,後面條件即使也成立也不會再檢查。
>= 含邊界本身;> 不含邊界,剛好卡在邊界值的題目一定要親手代入驗算。
「沒 break 不停,一路溜到底」——貫穿只看有沒有 break,不會重新比對 case 標籤的數字。
case 之間緊鄰無程式碼=刻意分組共用;case 中間夾程式碼卻無 break=多半是忘記寫。
沒指定值=前一個成員+1;有指定值=直接採用該數字,之後繼續從該數字+1遞增。
if(a=5) 是賦值(恆為真,因5非0);if(a==5) 才是比較,統測最愛考手誤陷阱。
先確認 flag 變數原本代表什麼意思,再檢查後續
!flag 等用法是否呼應這個語意,而不是先懷疑運算子。