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