您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“C++中如何使用switch語句”,內容詳細,步驟清晰,細節處理妥當,希望這篇“C++中如何使用switch語句”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
基本格式
switch語句的基本格式如下:
switch (表達式) {
case 常量表達式1:《語句序列1》《break;》 //《》中的內容可省
……
case 常量表達式n:《語句序列n》《break;》 //同上,下同
《default:語句序列》
}
其中:
表達式——稱為“條件表達式”,用作判斷條件,取值為整型、字符型、布爾型或枚舉型。
常量表達式——由常量構成,取值類型與條件表達式相同。
語句序列——可以是一個語句也可以是一組語句。
if語句與switch語句
相信學過C/C++的同學對這兩個語句的異同早就了如指掌,if語句作為條件判斷,滿足條件進入if語句塊,不滿足條件則進入else語句塊,而且if和else語句塊又可以繼續嵌套if語句。switch則是通過判斷一個整型表達式的值來決定進入到哪一個case語句中,如果所有case條件都不滿足則進入到default語句塊。
//簡單的if語句 if (a == 1) i = 1; else if (a == 2) i = 2; else i = 3;
//簡單的switch語句 switch (a){ case 1: i = 1; case 2: i = 2; default: i = 3; }
編譯器如何實現switch語句?
現在編譯器已經足夠智能和強大,經過測試,g++實現switch語句的方式就至少有三種,編譯器會根據代碼的實際情況,權衡時間效率和空間效率,去選擇一個對當前代碼而言綜合效率最高的一種。
編譯器實現switch語句的三種方式:
逐條件判斷
跳轉表
二分查找法
后面我們將就這三種實現方法適用的代碼場景進行測試和分析。
1. 逐條件判斷法
逐條件判斷法其實就是和if……else語句的匯編實現相同,編譯器把switch語句中各個case條件逐個進行判斷,直到找到正確的case語句塊。這種方法適用于switch語句中case條件很少的情況,即使逐個條件判斷也不會導致大量時間和空間的浪費,比如下面這段代碼:
#include <algorithm> int test_switch(){ int i ; int a = std::rand(); switch(a){ case 0: i = 0;break; case 1: i = 1;break; case 2: i = 2;break; default: i = 3;break; } return i; }
該代碼對應的匯編代碼如下:
movl -4(%rbp), %eax cmpl $1, %eax je .L3 cmpl $2, %eax je .L4 testl %eax, %eax jne .L8 movl $0, -8(%rbp) jmp .L6 .L3: movl $1, -8(%rbp) jmp .L6 .L4: movl $2, -8(%rbp) jmp .L6 .L8: movl $3, -8(%rbp) nop
eax寄存器存儲的是判斷條件值(對應于C++代碼中的a值),首先判斷a是否等于1,如果等于1則跳轉到.L3執行a==1對應的代碼段,然后判斷a是否等于2,如果等于2則跳轉到.L4執行a==2對應的代碼段……可能難理解的是第6行代碼testl %eax, %eax,其實這只是編譯器提高判斷一個寄存器是否為0效率的一個小技巧,如果eax不等于0則跳轉到.L8代碼段,執行default代碼段對應的代碼,如果eax等于0則執行a==0對應的代碼段。
由上面對編譯器生成匯編代碼的分析,我們可以發現:編譯器在這種情況下使用逐個條件判斷來實現switch語句。
2. 跳轉表實現法
在編譯器采用這種switch語句實現方式的時候,會在程序中生成一個跳轉表,跳轉表存放各個case語句指令塊的地址,程序運行時,首先判斷switch條件的值,然后把該條件值作為跳轉表的偏移量去找到對應case語句的指令地址,然后執行。這種方法適用于case條件較多,但是case的值比較連續的情況,使用這種方法可以提高時間效率且不會顯著降低空間效率,比如下面這段代碼編譯器就會采用跳轉表這種實現方式:
#include <algorithm> int test_switch(){ int i ; int a = std::rand(); switch(a){ case 0: i = 0;break; case 1: i = 1;break; case 2: i = 2;break; case 3: i = 3;break; case 4: i = 4;break; case 5: i = 5;break; case 6: i = 6;break; case 7: i = 7;break; case 8: i = 8;break; case 9: i = 9;break; default: i = 10;break; } return i; }
該代碼對應的匯編代碼如下:
movl -4(%rbp), %eax movq .L4(,%rax,8), %rax jmp *%rax .L4: .quad .L3 .quad .L5 .quad .L6 .quad .L7 .quad .L8 .quad .L9 .quad .L10 .quad .L11 .quad .L12 .quad .L13 .text .L3: movl $0, -8(%rbp) jmp .L14 .L5: movl $1, -8(%rbp) jmp .L14 #后面省略……
在x64架構中,eax寄存器是rax寄存器的低32位,此處我們可以認為兩者值相等,代碼第一行是把判斷條件(對應于C++代碼中的a值)復制到eax寄存器中,第二行代碼是把.L4段偏移rax寄存器值大小的地址賦值給rax寄存器,第三行代碼則是取出rax中存放的地址并且跳轉到該地址處。我們可以清楚的看到.L4代碼段就是編譯器為switch語句生成的存放于.text段的跳轉表,每種case均對應于跳轉表中一個地址值,我們通過判斷條件的值即可計算出來其對應代碼段地址存放的地址相對于.L4的偏移,從而實現高效的跳轉。
3. 二分查找法
如果case值較多且分布極其離散的話,如果采用逐條件判斷的話,時間效率會很低,如果采用跳轉表方法的話,跳轉表占用的空間就會很大,前兩種方法均會導致程序效率低。在這種情況下,編譯器就會采用二分查找法實現switch語句,程序編譯時,編譯器先將所有case值排序后按照二分查找順序寫入匯編代碼,在程序執行時則采二分查找的方法在各個case值中查找條件值,如果查找到則執行對應的case語句,如果最終沒有查找到則執行default語句。對于如下C++代碼編譯器就會采用這種二分查找法實現switch語句:
#include <algorithm> int test_switch(){ int i ; int a = std::rand(); switch(a){ case 4: i = 4;break; case 10: i = 10;break; case 50: i = 50;break; case 100: i = 100;break; case 200: i = 200;break; case 500: i = 500;break; default: i = 0;break; } return i; }
改代碼段對應的匯編代碼為:
movl -4(%rbp), %eax cmpl $50, %eax je .L3 cmpl $50, %eax jg .L4 cmpl $4, %eax je .L5 cmpl $10, %eax je .L6 jmp .L2 .L4: cmpl $200, %eax je .L7 cmpl $500, %eax je .L8 cmpl $100, %eax je .L9 jmp .L2
代碼第二行條件值首先與50比較,為什么是50而不是放在最前面的4?這是因為二分查找首先查找的是處于中間的值,所以這里先與50進行比較,如果eax等于50,則執行case
50對應代碼,如果eax值大于50則跳轉到.L4代碼段,如果eax小于50則繼續跟4比較……直至找到條件值或者查找完畢條件值不存在。可以看出二分查找法在保持了較高的查詢效率的同時又節省了空間占用。
讀到這里,這篇“C++中如何使用switch語句”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。