for循环优化_for 循环和while循环区别
C語言提供了好幾種循環(huán)結(jié)構(gòu),即while、for和do-while。匯編語言中并沒有相應(yīng)的指令存在,作為替代,將條件測試和跳轉(zhuǎn)組合起來實(shí)現(xiàn)循環(huán)的效果。大多數(shù)匯編器根據(jù)一個(gè)循環(huán)的do-while形式來產(chǎn)生循環(huán)代碼,即使在實(shí)際程序中這種形式用的相對(duì)較少。其它的循環(huán)會(huì)首先轉(zhuǎn)換成do-while形式,然后再編譯成機(jī)器代碼。
do-while循環(huán)
其通用形式是這樣的:
do body-statementwhile (test-expr);循環(huán)的效果就是重復(fù)執(zhí)行body-statement,對(duì)test-expr求值,如果求值的結(jié)果為非零,就繼續(xù)循環(huán)。注意,body-statement至少執(zhí)行一次。
do-while的通用形式可以翻譯成如下所示的條件和goto語句:
loop:
body-statement t = test-expr; if(t) goto loop;也就是說每次循環(huán)程序會(huì)執(zhí)行循環(huán)體里面的語句,然后執(zhí)行測試表達(dá)式。如果測試為真,則回去再執(zhí)行一次循環(huán)。
下面示例用do-while循環(huán)計(jì)算函數(shù)參數(shù)的階乘,寫作n!只計(jì)算n>0時(shí)候n階乘的值:
int fact_do(int n){ int result = 1; do { result *= n; n = n - 1; }while(n > 1); return result;}匯編代碼是do-while循環(huán)的一個(gè)實(shí)現(xiàn)形式,這里用的gcc編譯器
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)編譯參數(shù)是
$ gcc -m32 -O2 -o fact因?yàn)榉浅2涣?xí)慣AT&T匯編形式,所以這里用IDA pro 對(duì)得到的fact文件進(jìn)行反匯編分析,原文的匯編形式(用edx保存參數(shù)n)我無論怎么調(diào)節(jié)參數(shù)都無法得到。圖中是一個(gè)do-while循環(huán)的標(biāo)準(zhǔn)實(shí)現(xiàn),eax初始化為1,epb+8地址處保存著參數(shù)n,0x08048404 處把參數(shù)n減一,緊接著0x08048408 處把n與1比較。如果為真則在0x0804840C處跳回循環(huán)的開始,這里是循環(huán)的關(guān)鍵地方由它來判斷循環(huán)是繼續(xù)還是退出。
綜合0x080483F3,0x080483FA我們可以看到eax被初始化為1,在0x080483FD被乘法更新。如果學(xué)過x86匯編語言就知道 mul 乘法指令是離不開eax寄存器的,而且返回值通常也用eax寄存器。所以這里eax對(duì)應(yīng)于結(jié)果result是無懸念的。
理解產(chǎn)生的匯編代碼與原始代碼之間的關(guān)系,關(guān)鍵是找到程序值和寄存器之間的映射關(guān)系。對(duì)于循環(huán)fact_do來說,這個(gè)任務(wù)非常簡單,但是對(duì)于更復(fù)雜的程序來說,就可能是更具挑戰(zhàn)性的任務(wù)。C語言編譯器常常會(huì)重組計(jì)算,因此有些C代碼中的變量在機(jī)器代碼中沒有對(duì)應(yīng)的值;而有時(shí),機(jī)器代碼中又會(huì)引入源代碼中不存在的新值。此外編譯器還常常試圖將多個(gè)程序值映射到一個(gè)寄存器上,來最小化寄存器的使用率。 上面的fact_do的過程對(duì)于逆向工程循環(huán)來說,是一個(gè)通用的策略。看看在循環(huán)之前如何初始化寄存器,在循環(huán)中如何更新和測試寄存器,以及在循環(huán)之后又如何使用寄存器。這些步驟中的每一步都提供了一個(gè)線索,組合起來就可以解開謎團(tuán)。做好準(zhǔn)備,你會(huì)看到令人驚奇的變換,其中有些情況很明顯是編譯器能夠優(yōu)化的代碼,而有些情況很難解釋編譯器為什么要選用那些奇怪的策略。while循環(huán)
while語句的通用形式如下:
while(test-expr) body-statement與do-while不同的是,它對(duì)test-expr求值,在第一次執(zhí)行body-statement之前,循環(huán)就可能中止。將while循環(huán)翻譯成機(jī)器代碼有很多種方法。一種常見的方法,也就是GCC采用的方法,是使用條件分支,在需要時(shí)省略循環(huán)體的第一次執(zhí)行,從而將代碼轉(zhuǎn)換成do-while循環(huán),如下:
if(!test-expr) goto done;do body-statementwhile(test-expr);done:接下來這個(gè)代碼可直接翻譯成goto代碼,如下:
if t = test-expr if(!t) goto done;loop: body-statement t = test-expr; if(t) goto loop;done:使用這種策略,編譯器常常會(huì)優(yōu)化最開始的測試,比如說認(rèn)為總是滿足測試條件。
舉個(gè)例子fact_while是使用while循環(huán)的階乘函數(shù)的實(shí)現(xiàn),這個(gè)函數(shù)能正確的計(jì)算 0!=1 。fact_while_goto是GCC產(chǎn)生的匯編代碼的C語言翻譯,比較fact_do 和fact_while 我們看到它們幾乎是相同的。將while循環(huán)轉(zhuǎn)換成do-while循環(huán),以及將后者翻譯成goto代碼。
int fact_while(int n){ int result = 1; while(n > 1){ result *= n; n = n - 1; } return result;}int fact_while_goto(int n){ int result = 1; if(n <= 1) goto done; loop: result *= n; n = n - 1; if(n > 1) goto loop; done: return result;}for循環(huán)
for循環(huán)的通用形式如下
for(init-expr;test-expr;update-expr) body-statementC語言標(biāo)準(zhǔn)說明,這樣一個(gè)循環(huán)的行為與下面這段使用while循環(huán)代碼的行為一樣:
init-expr;while(test-expr) { body-statement update-expr;}程序首先對(duì)初始表達(dá)式init-expr求值,然后進(jìn)入循環(huán);在循環(huán)中它先對(duì)測試條件test-expr求值,如果測試結(jié)果為“假”就會(huì)退出,否則執(zhí)行循環(huán)體body-statement;最后對(duì)更新表達(dá)式update-expr求值。
這段代碼編譯后的形式,基于前面講過的從while到do-while的轉(zhuǎn)換,首先給出do-while的形式:
init-expr;if(!test-expr) goto done;do{ body-statement update-expr;}while(test-expr);done:然后將它轉(zhuǎn)換成goto代碼:
init-expr; t = test-expr if(!t) goto done;loop: body-statement update-expr; t = test-expr; if(t) goto loop;done:作為一個(gè)示例,考慮用for循環(huán)寫的階乘函數(shù):
int fact_for(int n){ int i; int result = 1; for(i = 2;i <= n; i ++) result *= i; return result;}如上述代碼所示,用for循環(huán)編寫階乘函數(shù)最自然的方式就是將從2一直到n的因子乘起來,因此這個(gè)函數(shù)與我們使用while或者do-while循環(huán)的代碼都不一樣。
這段代碼中for循環(huán)的不同組成部分如下:
用這些部分帶入前面給出的模板中的相應(yīng)位置,得到下面goto代碼的版本:
int fact_for_goto(int n){ int i = 2; int result = 1; if( !(i <=n ) ) goto done; loop: result *= i; i ++; if(i <= n) goto loop; done: return result;}確實(shí)仔細(xì)查看GCC產(chǎn)生的匯編代碼會(huì)發(fā)現(xiàn)非常接近如下形式:
綜上所述,C語言中三種形式的所有循環(huán)— do-while,while和for–都可以用一種簡單的策略來翻譯,產(chǎn)生包含一個(gè)或多個(gè)條件分支的代碼。控制的條件轉(zhuǎn)移為循環(huán)翻譯成機(jī)器代碼提供了基本機(jī)制。
死循環(huán)選擇for還是while
最后再說一下,看到有人在網(wǎng)上討論死循環(huán)用 for(;;); 好,還是用 while(1); 好。
自己親自測試了下,在 -O2 參數(shù)下它們生成的匯編指令是一樣的(看來這應(yīng)該跟優(yōu)化配置和編譯器選擇有很大關(guān)系)。
總結(jié)
以上是生活随笔為你收集整理的for循环优化_for 循环和while循环区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 未发现数据源名称_在Power BI中管
- 下一篇: python shelve模块_说说 P