網(wǎng)站首頁 編程語言 正文
流程控制語句是C語言中最基本的判斷語句,通常我們可以使用IF來構(gòu)建多分支結(jié)構(gòu),但同樣可以使用Switch語句構(gòu)建,Switch語句針對多分支的優(yōu)化措施有4種形式,分別是,IF-ELSE優(yōu)化,有序線性優(yōu)化,非線性索引優(yōu)化,平衡判定樹優(yōu)化。
與IF語句結(jié)構(gòu)不同,IF語句會在條件跳轉(zhuǎn)后緊跟語句塊,而SWITCH結(jié)構(gòu)則將所有條件跳轉(zhuǎn)都放置在一起,判斷時需要重點觀察每個條件跳轉(zhuǎn)指令后面是否跟有語句塊,以辨別SWITCH分支結(jié)構(gòu)。
在switch分支數(shù)小于4的情況下,編譯器將采用模擬IF-ELSE分支的方式構(gòu)建SWITCH結(jié)構(gòu),這樣則無法發(fā)揮出SWITCH語句的優(yōu)勢,當(dāng)分支數(shù)大于3并且case的判斷值存在明顯線性關(guān)系時,Switch語句的優(yōu)化特性才可以凸顯出來。
有序線性優(yōu)化: 該優(yōu)化方式將每個case語句塊的地址預(yù)先保存在數(shù)組中,并依據(jù)此數(shù)組查詢case語句塊對應(yīng)的首地址。
首先代碼生成時會為case語句制作一個case地址表數(shù)組,數(shù)組中保存每個ease語句塊的首地址,并且下標(biāo)以0開頭,在進(jìn)入switch后先進(jìn)行一次比較,檢查輸入的數(shù)值是否大于case值的最大值,
為了達(dá)到線性有序
,對于沒有case對應(yīng)的數(shù)值,編譯器以switch的結(jié)束地址或者default語句塊的首地址填充對應(yīng)的表格項。
case線性地址表是一個有序表。
當(dāng)switch為一個有序線性組合時,會對其case語句塊制作地址表,以減少比較跳轉(zhuǎn)次數(shù)。
在編寫代碼時,我們無需自己排列case序列,編譯器編譯時會自動進(jìn)行排序優(yōu)化,先來編寫一個簡單的代碼:
int main(int argc, char *argv[]) { int index = 0; scanf("%d", &index); switch (index) { case 1: printf("index 1"); break; case 2: printf("index 2"); break; case 3: printf("index 3"); break; case 6: printf("index 6"); break; case 7: printf("index 7"); break; default: printf("default"); break; } return 0; }
代碼經(jīng)過反匯編后,我們可以注意到,首先用戶通過scanf
輸入所需要執(zhí)行的分支,因為分支語句下標(biāo)從0開始,所以需要dec eax
減去1,在進(jìn)入switch語句之前,判斷輸入的下標(biāo)是否高于6,如果高于則直接跳出switch,否則執(zhí)行ds:[eax*4+0x401348]
尋址。
004012B4 | FF15 B8304000 ? ? ? ? ? ?| call dword ptr ds:[<&scanf>] ? ? ? ? ? ? ?|
004012BA | 8B45 FC ? ? ? ? ? ? ? ? ?| mov eax,dword ptr ss:[ebp-4] ? ? ? ? ? ? ?|
004012BD | 83C4 08 ? ? ? ? ? ? ? ? ?| add esp,8 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
004012C0 | 48 ? ? ? ? ? ? ? ? ? ? ? | dec eax ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | Switch語句獲取比例因子,需要減1
004012C1 | 83F8 06 ? ? ? ? ? ? ? ? ?| cmp eax,6 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | 首先對比輸入數(shù)據(jù)是否大于6
004012C4 | 77 6B ? ? ? ? ? ? ? ? ? ?| ja consoleapplication.401331 ? ? ? ? ? ? ?| 大于則說明Switch分支無對應(yīng)結(jié)構(gòu) (則直接跳轉(zhuǎn)到結(jié)束)
004012C6 | FF2485 48134000 ? ? ? ? ?| jmp dword ptr ds:[eax*4+0x401348] ? ? ? ? | 跳轉(zhuǎn)到指定代碼段
地址0x401348對應(yīng)的就是每一個分支的首地址,跳轉(zhuǎn)后即可看到分支。
非線性索引優(yōu)化: 如果兩個case值間隔較大,仍然使用switch的結(jié)尾地址或default地址代替地址表中缺少的case地址,這樣則會造成極大的空間浪費。
非線性的switch結(jié)構(gòu),可采用制作索引表的方式進(jìn)行優(yōu)化,索引表有兩張,1.case語句塊地址表,2.case語句塊索引表。
地址表中每一項保存一個case語句塊首地址,有幾個case語句塊或default語句塊,就保存幾項,結(jié)束地址在地址表中指揮保存一份。
索引表中保存了地址表中的下標(biāo)值,索引表最多可容納256項,每項1字節(jié),所以case值不可超過1字節(jié),索引表也只能存儲256項索引編號。
在執(zhí)行時需要通過索引表來查詢地址表,會多出一次查表的過程,因此效率上會有所下降。
004012B4 | FF15 B8304000 ? ? ? ? ? ?| call dword ptr ds:[<&scanf>] ? ? ? ? ? ? ?|
004012BA | 8B45 FC ? ? ? ? ? ? ? ? ?| mov eax,dword ptr ss:[ebp-4] ? ? ? ? ? ? ?|
004012BD | 83C4 08 ? ? ? ? ? ? ? ? ?| add esp,8 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
004012C0 | 48 ? ? ? ? ? ? ? ? ? ? ? | dec eax ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | Switch語句獲取比例因子,需要減1
004012C1 | 3D FE000000 ? ? ? ? ? ? ?| cmp eax,FE ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| 首先對比輸入數(shù)據(jù)是否大于254
004012C6 | 0F87 80000000 ? ? ? ? ? ?| ja consoleapplication.40134C ? ? ? ? ? ? ?| 跳轉(zhuǎn)到指定代碼段
004012CC | 0FB680 70134000 ? ? ? ? ?| movzx eax,byte ptr ds:[eax+0x401370] ? ? ?| 從索引表找地址表下標(biāo)
004012D3 | FF2485 54134000 ? ? ? ? ?| jmp dword ptr ds:[eax*4+0x401354] ? ? ? ? | 比例因子尋找函數(shù)地址
首先movzx eax, byte ptr ds:[eax+0x401370]
從索引表中找到地址表下標(biāo)。
然后通過索引表找到索引值,并帶入jmp dword ptr ds:[eax*4+0x401354]
找到地址表中的指定地址,地址表中每一個地址就代表一個分支結(jié)構(gòu)里的函數(shù)。
這樣的優(yōu)勢就是可以節(jié)約空間,每一個所以表字段只占1字節(jié),如果兩個case差距較大同樣會指向同一個地址表中的地址,地址表相對來說會變得簡單,但這種查詢方式會產(chǎn)生兩次間接內(nèi)存訪問,在效率上遠(yuǎn)遠(yuǎn)低于線性表方式。
平衡判定樹優(yōu)化: 當(dāng)最大case值與最小case值之差大于255時,則會采用判定樹優(yōu)化,將每個case值作為一個節(jié)點,從節(jié)點中找出中間值作為根節(jié)點,以此形成一顆平衡二叉樹,以每個節(jié)點為判定值,大于和小于關(guān)系分別對應(yīng)左子樹和右子樹,從而提高查詢效率。
如果打開編譯器體積優(yōu)先,編譯器盡量會以二叉判定樹的方式來降低程序占用體積,如果無法使用以上優(yōu)化方式,則需要將switch做成樹。
int main(int argc, char *argv[]) { int index = 0; scanf("%d", &index); switch (index) { case 2: printf("index 2"); break; case 3: printf("index 3"); break; case 8: printf("index 8"); break; case 10: printf("index 10"); break; case 35: printf("index 35"); break; case 37: printf("index 37"); break; case 666: printf("index 666"); break; } return 0; }
判定樹反匯編形式。
004012C0 | 83F8 0A ? ? ? ? ? ? ? ? ?| cmp eax,A ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | A:'\n'
004012C3 | 7F 63 ? ? ? ? ? ? ? ? ? ?| jg consoleapplication.401328 ? ? ? ? ? ? ?|
004012C5 | 74 4D ? ? ? ? ? ? ? ? ? ?| je consoleapplication.401314 ? ? ? ? ? ? ?|
004012C7 | 83E8 02 ? ? ? ? ? ? ? ? ?| sub eax,2 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
004012CA | 74 34 ? ? ? ? ? ? ? ? ? ?| je consoleapplication.401300 ? ? ? ? ? ? ?|
004012CC | 48 ? ? ? ? ? ? ? ? ? ? ? | dec eax ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
004012CD | 74 1D ? ? ? ? ? ? ? ? ? ?| je consoleapplication.4012EC ? ? ? ? ? ? ?|
004012CF | 83E8 05 ? ? ? ? ? ? ? ? ?| sub eax,5 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
004012D2 | 0F85 97000000 ? ? ? ? ? ?| jne consoleapplication.40136F ? ? ? ? ? ? |
004012D8 | 68 A0314000 ? ? ? ? ? ? ?| push consoleapplication.4031A0 ? ? ? ? ? ?| main.cpp:16, 4031A0:"index 8"
004012DD | FF15 B4304000 ? ? ? ? ? ?| call dword ptr ds:[<&printf>] ? ? ? ? ? ? | main.cpp:20
004012E3 | 83C4 04 ? ? ? ? ? ? ? ? ?| add esp,4 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
判定樹,通過增加多條分支結(jié)構(gòu),從中位數(shù)開始判斷,大于或小于分別走不同的分支,直到遇到函數(shù)地址位置。
為了降低數(shù)的高度,在優(yōu)化過程中,會檢查代碼是否滿足if-else優(yōu)化,有序線性優(yōu)化,非線性索引優(yōu)化,利用三種優(yōu)化來降低樹高度,誰的效率高就優(yōu)先使用誰,三種優(yōu)化都無法匹配才會使用判定樹。
原文鏈接:https://www.cnblogs.com/LyShark/archive/2022/01/28/15852569.html
相關(guān)推薦
- 2022-01-17 報錯:是否需要更改目標(biāo)庫?請嘗試將lib編譯器選項更改為es2015或更高版本
- 2022-03-29 在pyqt5中展示pyecharts生成的圖像問題_python
- 2022-07-16 Linux查看日志的幾種方式
- 2022-05-17 qt 解決Error while building/deploying project Hmi (k
- 2022-10-29 .Net?Core?配置文件讀取IOptions,IOptionsMonitor,IOptionsS
- 2022-04-17 將本地jar放到pom中被maven管理
- 2022-10-11 React -配置文件中需要使用組件中異步請求到的數(shù)據(jù)
- 2022-07-19 使用普通指針實現(xiàn)數(shù)組倒敘和字符串的壓縮
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支