3. for語句

前兩節我們在whiledo/while循環中使用循環變數,其實使用循環變數最見的是for循環這種形式。for語句的語法是:

for (控製表達式1; 控製表達式2; 控製表達式3) 語句

如果不考慮循環體中包含continue語句的情況(稍後介紹continue語句),這個for循環等價于下面的while循環:

控製表達式1;
while (控製表達式2) {
	語句
	控製表達式3;
}

從這種等價形式來看,控製表達式1和3都可以為空,但控製表達式2是必不可少的,例如for (;1;) {...}等價于while (1) {...}死循環。C語言規定,如果控製表達式2為空,則認為控製表達式2的值為真,因此死循環也可以寫成for (;;) {...}

上一節do/while循環的例子可以改寫成for循環:

int factorial(int n)
{
	int result = 1;
	int i;
	for(i = 1; i <= n; ++i)
		result = result * i;
	return result;
}

其中++i這個表達式相當於i = i + 1[9],++稱為首碼自增運算符(Prefix Increment Operator),類似地,--稱為首碼自減運算符(Prefix Decrement Operator)[10]--i相當於i = i - 1。如果把++i這個表達式看作一個函數調用,除了傳入一個參數返回一個值(等於參數值加1)之外,還產生一個Side Effect,就是把變數i的值增加了1。

++--運算符也可以用在變數後面,例如i++i--,為了和首碼運算符區別,這兩個運算符稱為尾碼自增運算符(Postfix Increment Operator)和尾碼自減運算符(Postfix Decrement Operator)。如果把i++這個表達式看作一個函數調用,傳入一個參數返回一個值,返回值就等於參數值(而不是參數值加1),此外也產生一個Side Effect,就是把變數i的值增加了1,它和++i的區別就在於返回值不同。同理,--i返回減1之後的值,而i--返回減1之前的值,但這兩個表達式都產生同樣的Side Effect,就是把變數i的值減了1。

使用++、--運算符會使程序更加簡潔,但也會影響程序的可讀性,[K&R]中的示例代碼大量運用++、--和其它表達式的組合使得代碼非常簡潔。為了讓初學者循序漸進,在接下來的幾章中++、--運算符總是單獨組成一個表達式而不跟其它表達式組合,從第 11 章 排序與查找開始將採用[K&R]的簡潔風格。

我們看一個有意思的問題:a+++++b這個表達式如何理解?應該理解成a++ ++ +b還是a++ + ++b,還是a + ++ ++b呢?應該按第一種方式理解。編譯的過程分為詞法解析和語法解析兩個階段,在詞法解析階段,編譯器總是從前到後找最長的合法Token。把這個表達式從前到後解析,變數名a是一個Token,a後面有兩個以上的+號,在C語言中一個+號是合法的Token(可以是加法運算符或正號),兩個+號也是合法的Token(可以是自增運算符),根據最長匹配原則,編譯器絶不會止步于一個+號,而一定會把兩個+號當作一個Token。再往後解析仍然有兩個以上的+號,所以又是一個++運算符。再往後解析只剩一個+號了,是加法運算符。再往後解析是變數名b。詞法解析之後進入下一階段語法解析,a是一個表達式,表達式++還是表達式,表達式再++還是表達式,表達式再+b還是表達式,語法上沒有問題。最後編譯器會做一些基本的語義分析,這時就有問題了,++運算符要求操作數能做左值,a能做左值所以a++沒問題,但表達式a++的值只能做右值,不能再++了,所以最終編譯器會報錯。

C99規定了一種新的for循環語法,在控製表達式1的位置可以有變數定義。例如上例的循環變數i可以只在for循環中定義:

int factorial(int n)
{
	int result = 1;
	for(int i = 1; i <= n; i++)
		result = result * i;
	return result;
}

如果這樣定義,那麼變數i只是for循環中的局部變數而不是整個函數的局部變數,相當於第 1 節 “if語句”講過的語句塊中的局部變數,在循環結束後就不能再使用i這個變數了。這個程序用gcc編譯要加上選項-std=c99。這種語法也是從C++借鑒的,考慮到兼容性不建議使用這種寫法。



[9] 這兩種寫法在語義上稍有區別,詳見第 2.1 節 “復合賦值運算符”

[10] increment和decrement這兩個詞很有意思,大多數字典都說它們是名詞,但經常被當成動詞用,在計算機術語中,它們當動詞用應該理解為increase by one和decrease by one。現代英語中很多原本是名詞的都被當成動詞用,字典都跟不上時代了,再比如transition也是如此。