C++?反匯編之關(guān)于Switch語(yǔ)句的優(yōu)化措施
流程控制語(yǔ)句是C語(yǔ)言中最基本的判斷語(yǔ)句,通常我們可以使用IF來(lái)構(gòu)建多分支結(jié)構(gòu),但同樣可以使用Switch語(yǔ)句構(gòu)建,Switch語(yǔ)句針對(duì)多分支的優(yōu)化措施有4種形式,分別是,IF-ELSE優(yōu)化,有序線性優(yōu)化,非線性索引優(yōu)化,平衡判定樹優(yōu)化。
與IF語(yǔ)句結(jié)構(gòu)不同,IF語(yǔ)句會(huì)在條件跳轉(zhuǎn)后緊跟語(yǔ)句塊,而SWITCH結(jié)構(gòu)則將所有條件跳轉(zhuǎn)都放置在一起,判斷時(shí)需要重點(diǎn)觀察每個(gè)條件跳轉(zhuǎn)指令后面是否跟有語(yǔ)句塊,以辨別SWITCH分支結(jié)構(gòu)。
在switch分支數(shù)小于4的情況下,編譯器將采用模擬IF-ELSE分支的方式構(gòu)建SWITCH結(jié)構(gòu),這樣則無(wú)法發(fā)揮出SWITCH語(yǔ)句的優(yōu)勢(shì),當(dāng)分支數(shù)大于3并且case的判斷值存在明顯線性關(guān)系時(shí),Switch語(yǔ)句的優(yōu)化特性才可以凸顯出來(lái)。
有序線性優(yōu)化: 該優(yōu)化方式將每個(gè)case語(yǔ)句塊的地址預(yù)先保存在數(shù)組中,并依據(jù)此數(shù)組查詢case語(yǔ)句塊對(duì)應(yīng)的首地址。
首先代碼生成時(shí)會(huì)為case語(yǔ)句制作一個(gè)case地址表數(shù)組,數(shù)組中保存每個(gè)ease語(yǔ)句塊的首地址,并且下標(biāo)以0開頭,在進(jìn)入switch后先進(jìn)行一次比較,檢查輸入的數(shù)值是否大于case值的最大值,
為了達(dá)到線性有序,對(duì)于沒有case對(duì)應(yīng)的數(shù)值,編譯器以switch的結(jié)束地址或者default語(yǔ)句塊的首地址填充對(duì)應(yīng)的表格項(xiàng)。
case線性地址表是一個(gè)有序表。
當(dāng)switch為一個(gè)有序線性組合時(shí),會(huì)對(duì)其case語(yǔ)句塊制作地址表,以減少比較跳轉(zhuǎn)次數(shù)。
在編寫代碼時(shí),我們無(wú)需自己排列case序列,編譯器編譯時(shí)會(huì)自動(dòng)進(jìn)行排序優(yōu)化,先來(lái)編寫一個(gè)簡(jiǎn)單的代碼:
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í)行的分支,因?yàn)榉种дZ(yǔ)句下標(biāo)從0開始,所以需要dec eax減去1,在進(jìn)入switch語(yǔ)句之前,判斷輸入的下標(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語(yǔ)句獲取比例因子,需要減1
004012C1 | 83F8 06 | cmp eax,6 | 首先對(duì)比輸入數(shù)據(jù)是否大于6
004012C4 | 77 6B | ja consoleapplication.401331 | 大于則說(shuō)明Switch分支無(wú)對(duì)應(yīng)結(jié)構(gòu) (則直接跳轉(zhuǎn)到結(jié)束)
004012C6 | FF2485 48134000 | jmp dword ptr ds:[eax*4+0x401348] | 跳轉(zhuǎn)到指定代碼段
地址0x401348對(duì)應(yīng)的就是每一個(gè)分支的首地址,跳轉(zhuǎn)后即可看到分支。

非線性索引優(yōu)化: 如果兩個(gè)case值間隔較大,仍然使用switch的結(jié)尾地址或default地址代替地址表中缺少的case地址,這樣則會(huì)造成極大的空間浪費(fèi)。
非線性的switch結(jié)構(gòu),可采用制作索引表的方式進(jìn)行優(yōu)化,索引表有兩張,1.case語(yǔ)句塊地址表,2.case語(yǔ)句塊索引表。
地址表中每一項(xiàng)保存一個(gè)case語(yǔ)句塊首地址,有幾個(gè)case語(yǔ)句塊或default語(yǔ)句塊,就保存幾項(xiàng),結(jié)束地址在地址表中指揮保存一份。
索引表中保存了地址表中的下標(biāo)值,索引表最多可容納256項(xiàng),每項(xiàng)1字節(jié),所以case值不可超過1字節(jié),索引表也只能存儲(chǔ)256項(xiàng)索引編號(hào)。
在執(zhí)行時(shí)需要通過索引表來(lái)查詢地址表,會(huì)多出一次查表的過程,因此效率上會(huì)有所下降。
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語(yǔ)句獲取比例因子,需要減1
004012C1 | 3D FE000000 | cmp eax,FE | 首先對(duì)比輸入數(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]找到地址表中的指定地址,地址表中每一個(gè)地址就代表一個(gè)分支結(jié)構(gòu)里的函數(shù)。

這樣的優(yōu)勢(shì)就是可以節(jié)約空間,每一個(gè)所以表字段只占1字節(jié),如果兩個(gè)case差距較大同樣會(huì)指向同一個(gè)地址表中的地址,地址表相對(duì)來(lái)說(shuō)會(huì)變得簡(jiǎn)單,但這種查詢方式會(huì)產(chǎn)生兩次間接內(nèi)存訪問,在效率上遠(yuǎn)遠(yuǎn)低于線性表方式。
平衡判定樹優(yōu)化: 當(dāng)最大case值與最小case值之差大于255時(shí),則會(huì)采用判定樹優(yōu)化,將每個(gè)case值作為一個(gè)節(jié)點(diǎn),從節(jié)點(diǎn)中找出中間值作為根節(jié)點(diǎn),以此形成一顆平衡二叉樹,以每個(gè)節(jié)點(diǎn)為判定值,大于和小于關(guān)系分別對(duì)應(yīng)左子樹和右子樹,從而提高查詢效率。
如果打開編譯器體積優(yōu)先,編譯器盡量會(huì)以二叉判定樹的方式來(lái)降低程序占用體積,如果無(wú)法使用以上優(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)化過程中,會(huì)檢查代碼是否滿足if-else優(yōu)化,有序線性優(yōu)化,非線性索引優(yōu)化,利用三種優(yōu)化來(lái)降低樹高度,誰(shuí)的效率高就優(yōu)先使用誰(shuí),三種優(yōu)化都無(wú)法匹配才會(huì)使用判定樹。
到此這篇關(guān)于C++ 反匯編之關(guān)于Switch語(yǔ)句的優(yōu)化措施的文章就介紹到這了,更多相關(guān)C++ 反匯編Switch語(yǔ)句優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)學(xué)生選課系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)學(xué)生選課系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
實(shí)例講解在C++的函數(shù)中變量參數(shù)及默認(rèn)參數(shù)的使用
這篇文章主要介紹了在C++的函數(shù)中變量參數(shù)及默認(rèn)參數(shù)的使用,是C++函數(shù)入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2016-01-01
C語(yǔ)言基礎(chǔ)應(yīng)用處理學(xué)生打分?計(jì)算時(shí)間?最少硬幣問題詳細(xì)過程
很多的問題其實(shí)可以用編程來(lái)解決作答,本篇文章帶你用C語(yǔ)言解決最少硬幣問題、計(jì)算已經(jīng)過去了多久、學(xué)生成績(jī)自動(dòng)打分來(lái)做基礎(chǔ)的訓(xùn)練2022-02-02
C語(yǔ)言使用ffmpeg實(shí)現(xiàn)單線程異步的視頻播放器
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言如何使用ffmpeg實(shí)現(xiàn)單線程異步的視頻播放器功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-12-12
C語(yǔ)言強(qiáng)制類型轉(zhuǎn)換規(guī)則實(shí)例詳解
強(qiáng)制類型轉(zhuǎn)換是把變量從一種類型轉(zhuǎn)換為另一種數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言強(qiáng)制類型轉(zhuǎn)換的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06
解析C++函數(shù)的默認(rèn)參數(shù)和占位參數(shù)及較之C語(yǔ)言的拓展
這篇文章主要介紹了C++中的默認(rèn)參數(shù)和占位參數(shù)及較之C語(yǔ)言的拓展,需要的朋友可以參考下2016-03-03
基于WTL 雙緩沖(double buffer)繪圖的分析詳解
本篇文章是對(duì)WTL下使用雙緩沖(double buffer)繪圖進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C語(yǔ)言實(shí)現(xiàn)關(guān)機(jī)小程序
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)關(guān)機(jī)小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02

