[C語言] - switch使用探討

switch為C提供的條件判斷式,只能用來比較數值或字元。ANSI C標準規定switch裡面的case至少需支援257個成員,因為字元長度為8-bit (256個可用字元 + EOF)

貫穿 (Fall Through)

貫穿(Fall Through)指的是當switch進到特定的case中執行完動作後並不會自動break,執行流程會繼續往下跑直到看到break聲明。某些場合我們會使用貫穿的特性讓程式較精簡,但使用此特性的場合並不多,因此仍然必須仔細確認程式流程

1
2
3
4
5
6
switch (2) {
case 1: printf("case 1\n");
case 2: printf("case 2\n");
case 3: printf("case 3\n");
default: printf("default\n");
}

此段程式碼結果如下,由case2貫穿到default

Break

Break除了使用在switch來跳出外,也可用在迴圈中的跳出,也因此可能造成非預期的流程但編譯器無法檢查出來

此段程式在if (y == OTHER_STUFF)後接了break(顯然是coding上有錯誤),原本是預期break之後可以執行initialize_modes_pointer();但因為此break在case2裡,且也不在迴圈裡面,因此會直接跳離此switch判斷式造成非預期的行為

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
switch (2) {
case 1:
doit1();
break;
case 2:
if (x == STUFF) {
do_first_stuff();
if (y == OTHER_STUFF)
break;
do_later_stuff();
} /* coder meant to break to here... */
initialize_modes_pointer();
break;
default:
processing();
}

Default

當所有的case都不滿足時會進入default條件裡面(如果有的話),通常我們習慣將default放在最後一個判斷,但其實default是可以放在任何位置的

1
2
3
4
5
6
7
8
switch (4) {
case 1: printf("case 1\n");
break;
default: printf("default\n");
case 2: printf("case 2\n");
break;
case 3: printf("case 3\n");
}

此段程式,然仍能夠正確的達到預期的結果

這時衍伸出一個有趣的問題,如果說default可放在任意位置,那是否代表switch在CPU執行時的行為和if…else並不相同,而是比較接近於goto的使用呢?

我們在x86的機器上將以上的程式碼編譯成組合語言

mov    DWORD PTR tv64[ebp], 4

將數字4移到ebp

cmp    DWORD PTR tv64[ebp], 1

將數字1和ebp比較

je    SHORT $LN4@main

若相等則跳到LN4


因為ebp皆不等於1,2,3因此最終會跳到LN3,另外default裡沒有break,從組合語言可以看到最終沒有接jmp,因此會繼續往下跑執行case2最終執行jmp到LN5

由此可見switch是由建立多個label+jmp到特地位置,而if…else則是經過不斷的cmp+jmp,兩者並不相同