|
for循環(huán)是我最喜歡的循環(huán)結(jié)構(gòu)了,本來以為我對for循環(huán)已經(jīng)很了解了,但在最近是使用之中不斷的出問題,所以我又對for循環(huán)進(jìn)行了一次比較深入的“研究”,研究結(jié)果使我大吃一驚,不得不感嘆,C語言真的是高深莫測。
好了,感慨完了,讓我們從頭開始來聊一聊這個最熟悉但又最難以捉摸的for循環(huán)吧。
for循環(huán)是C語言中最基本的循環(huán)結(jié)構(gòu)了,其典型應(yīng)用是在已知循環(huán)次數(shù)時,進(jìn)行的一系列循環(huán)操作;菊Z法格式舉例如下:
for(i=0;i<=100;i++)
括號中有三個表達(dá)式,其中第一個表達(dá)式用來給循環(huán)計數(shù)器賦初值,第二個表達(dá)式用來判斷是否滿足循環(huán)條件,第三個表達(dá)式用來改變循環(huán)計數(shù)器的值。
當(dāng)然,這只是最基本的用法,這三個表達(dá)式并非只能針對同一個變量,甚至,并非每個都必須出現(xiàn),這只是在循環(huán)體的不同位置進(jìn)行運(yùn)算的三個普通表達(dá)式而已。例如下面,計算n的階乘,直到大于100為止:
a=1;
for(i=1;a<=100;i++) a*=i;
另外,每個表達(dá)式的位置也并非只能放置一個表達(dá)式,要知道,C語言中有一種逗號表達(dá)式,用逗號將多個表達(dá)式分隔開,在處理上,當(dāng)做一個表達(dá)式來對待,最右邊的表達(dá)式運(yùn)算結(jié)果即為整個表達(dá)式的值;诖死碚,上面的階乘運(yùn)算可以改寫為下面的格式:
for(a=1,i=1;a<=100;i++) a*=i;
這樣寫有什么好處呢?當(dāng)然絕不僅僅是為了扮酷,在結(jié)構(gòu)上,能夠?qū)⒁粋整體運(yùn)算緊密的結(jié)合在一起,可以最大限度的減小程序修改時遺漏等失誤。Ctrl+C和Ctrl+V應(yīng)該在寫程序及修改程序中經(jīng)常用到吧,如果寫成這樣,在復(fù)制過程中想丟掉語句都難。
第二個表達(dá)式可以寫成逗號表達(dá)式的形式嗎?當(dāng)然也可以,不過我現(xiàn)在先不舉例,待會會和一些注意事項一起說明,F(xiàn)在我們先把for循環(huán)的結(jié)構(gòu)剖析一下,看看for循環(huán)究竟是怎樣運(yùn)行的。
首先是第一個表達(dá)式,這個表達(dá)式雖然是在for循環(huán)的循環(huán)表達(dá)式中出現(xiàn)的,但卻不在循環(huán)體內(nèi),其實是循環(huán)體前面緊鄰循環(huán)體的一個表達(dá)式,這也是上面兩個寫法效果相同的原因。畢竟它本身就是在循環(huán)體外面的,前一個程序只是光明正大的給寫在了外面。為了證實這一點(diǎn),我們來看一下for循環(huán)的匯編代碼(不同編譯器可能會有所不同,這里是在Keil4.1下編譯的,編譯器版本是armcc 4.0.0.728,選擇的處理器是8962,因此編譯出來的匯編指令是ARM CortexM3的指令)。
第一種寫法:
4: a=1;
0x000001A8 2101 MOVS r1,#0x01
5: for(i=1;a<=100;i++) a*=i;
0x000001AA 2201 MOVS r2,#0x01
0x000001AC E001 B 0x000001B2
0x000001AE 4351 MULS r1,r2,r1
0x000001B0 1C52 ADDS r2,r2,#1
0x000001B2 2964 CMP r1,#0x64
0x000001B4 DDFB BLE 0x000001AE
6: }
第二種寫法:
4: for(a=1,i=1;a<=100;i++) a*=i;
0x000001A8 2101 MOVS r1,#0x01
0x000001AA 2201 MOVS r2,#0x01
0x000001AC E001 B 0x000001B2
0x000001AE 4351 MULS r1,r2,r1
0x000001B0 1C52 ADDS r2,r2,#1
0x000001B2 2964 CMP r1,#0x64
0x000001B4 DDFB BLE 0x000001AE
5: }
可以看出,二者編譯出來的匯編代碼完全一樣。在這里,B是跳轉(zhuǎn)指令,在跳轉(zhuǎn)指令下面是循環(huán)體。循環(huán)體代碼如下:
0x000001AE 4351 MULS r1,r2,r1
0x000001B0 1C52 ADDS r2,r2,#1
0x000001B2 2964 CMP r1,#0x64
0x000001B4 DDFB BLE 0x000001AE
了解一些匯編的不難看出來(我也是在驗證這個for循環(huán)時看了一點(diǎn)ARM匯編,我參考的文檔是Cortex-M3 Technical Reference Manual),BLE是條件跳轉(zhuǎn),根據(jù)條件跳轉(zhuǎn)到前面的某一行語句上,循環(huán)的基本寫法。
C語言中對for循環(huán)的執(zhí)行過程描述如下:首先計算一次表達(dá)式1的值(參考格式:for(表達(dá)式1;表達(dá)式2;表達(dá)式3),再計算表達(dá)式2的值,如果表達(dá)式2的值為true,則執(zhí)行一次循環(huán)體,如果表達(dá)式2的值為false,則退出循環(huán)體。沒執(zhí)行完一次循環(huán)體后,計算表達(dá)式3的值,然后再計算表達(dá)式2的值,并根據(jù)表達(dá)式2的值決定是否繼續(xù)執(zhí)行循環(huán)體。
在這里表達(dá)式2需要在兩個位置計算,一是剛進(jìn)入循環(huán)時判斷第一次循環(huán)是否執(zhí)行,另外則是在每次執(zhí)行完循環(huán)時判斷是否進(jìn)行下一次循環(huán),一個位置是在整個循環(huán)體前,另一個位置是在表達(dá)式3之后。在編譯過程中,為了減小程序體積,表達(dá)式2只在表達(dá)式3之后計算。同時在循環(huán)體前增加一個無條件跳轉(zhuǎn)指令跳過整個循環(huán)體(包括表達(dá)式3)跳轉(zhuǎn)到循環(huán)的結(jié)尾來做第一次判斷。所以,for循環(huán)的執(zhí)行是從最后的指令開始執(zhí)行的。
總結(jié)一下,for循環(huán)中的三個表達(dá)式執(zhí)行方式如下:表達(dá)式1在整個循環(huán)體的前面(循環(huán)體外)執(zhí)行一次,表達(dá)式2為循環(huán)體的最后一組指令(可以有多個表達(dá)式組成,下同),表達(dá)式3為倒數(shù)第二組指令,在表達(dá)式2前面緊鄰表達(dá)式執(zhí)行。
注:關(guān)于循環(huán)體的定義,在C語言表達(dá)式中,for循環(huán)的三個表達(dá)式都不屬于循環(huán)體,但是假如以匯編的跳轉(zhuǎn)指令界定的話,表達(dá)式2和表達(dá)式3也應(yīng)該屬于循環(huán)體,畢竟它們也是以同樣的次數(shù)循環(huán)執(zhí)行的。以上只是為了說明我在文中使用的循環(huán)體名稱,很久不玩匯編了,對一些定義也都忘記了,所以也可能官方定義不同。
by 柳葉舟 |
|