switch與if..else 的執行的效率問題
今天讀一前輩的程式,發現其在串列埠中斷裡面為了分析協議的報文型別,在中斷函式裡面使用if..else語句。因為報文型別在現在看來只有兩種,以後有可能還會增加,不確定。 本人以為這樣用有些不妥,為什麼不用switch語句呢?猜想是不是因為效率方面的考慮呢,畢竟我們應該儘量是中斷的處理程式碼更加簡潔,時間效率更高才好。 所以本人就查詢相關資料,資料顯示switch語句反而比ifelse的執行效率要高。 下面來詳細描述switch與ifelse的區別。 switch...case與if...else的根本區別在於,switch...case會生成一個跳轉表來指示實際的case分支的地址,而這個跳轉表的索引號與switch變數的值是相等的。從而,switch...case不用像if...else那樣遍歷條件分支直到命中條件,而只需訪問對應索引號的表項從而到達定位分支的目的。 具體地說,switch...case會生成一份大小(表項數)為最大case常量+1的跳錶,程式首先判斷switch變數是否大於最大case 常量,若大於,則跳到default分支處理;否則取得索引號為switch變數大小的跳錶項的地址(即跳錶的起始地址+表項大小*索引號),程式接著跳到此地址執行,到此完成了分支的跳轉。 //
int main() { unsigned int i,j; i=3; switch (i) { case 0: j=0; break; case 1: j=1; break; case 2: j=2; break; case 3: j=3; break; case 4: j=4; break; default: j=10; break; }
}
用gcc編譯器,生成彙編程式碼(不開編譯器優化) .file "shiyan.c" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $20, %esp movl $3, -8(%ebp) cmpl $4, -8(%ebp) ja .L2 movl -8(%ebp), %eax sall $2, %eax movl .L8(%eax), %eax jmp *%eax .section .rodata .align 4 .align 4 .L8: .long .L3 .long .L4 .long .L5 .long .L6 .long .L7 .text .L3: movl $0, -12(%ebp) jmp .L11 .L4: movl $1, -12(%ebp) jmp .L11 .L5: movl $2, -12(%ebp) jmp .L11 .L6: movl $3, -12(%ebp) jmp .L11 .L7: movl $4, -12(%ebp) jmp .L11 .L2: movl $10, -12(%ebp) .L11: addl $20, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3" .section .note.GNU-stack,"",@progbits
由此看來,switch有點以空間換時間的意思,而事實上也的確如此。 1.當分支較多時,當時用switch的效率是很高的。因為switch是隨機訪問的,就是確定了選擇值之後直接跳轉到那個特定的分支,但是if。。else是遍歷所以得可能值,知道找到符合條件的分支。如此看來,switch的效率確實比ifelse要高的多。 2.由上面的彙編程式碼可知道,switch...case佔用較多的程式碼空間,因為它要生成跳錶,特別是當case常量分佈範圍很大但實際有效值又比較少的情況,switch...case的空間利用率將變得很低。 3.switch...case只能處理case為常量的情況,對非常量的情況是無能為力的。例如 if (a > 1 && a < 100),是無法使用switch...case來處理的。所以,switch只能是在常量選擇分支時比ifelse效率高,但是ifelse能應用於更多的場合,ifelse比較靈活。
由此看來,上面前輩的中斷處理程式中用switch是比較合適的,即節省了時間,而且對於以後程式的擴充套件也是很方便。因為報文型別這個值基本上都是用整形常量來表示的。