2. if/else語句

if語句還可以帶一個else子句(Clause),例如:

if (x % 2 == 0)
	printf("x is even.\n");
else
	printf("x is odd.\n");

這裡的%是取模(Modulo)運算符,x%2表示x除以2所得的餘數(Remainder),C語言規定%運算符的兩個操作數必須是整型的。兩個正數相除取餘數很好理解,如果操作數中有負數,結果應該是正是負呢?C99規定,如果ab是整型,b不等於0,則表達式(a/b)*b+a%b的值總是等於a,再結合第 5 節 “表達式”講過的整數除法運算要Truncate Toward Zero,可以得到一個結論:%運算符的結果總是與被除數同號(想一想為什麼)。其它編程語言對取模運算的規定各不相同,也有規定結果和除數同號的,也有不做明確規定的。

取模運算在程序中是非常有用的,例如上面的例子判斷x的奇偶性(Parity),看x除以2的餘數是不是0,如果是0則打印x is even.,如果不是0則打印x is odd.,讀者應該能看出else在這裡的作用了,如果在上面的例子中去掉else,則不管x是奇是偶,printf("x is odd.\n");總是執行。為了讓這條語句更有用,可以把它封裝(Encapsulate)成一個函數:

void print_parity(int x)
{
	if (x % 2 == 0)
		printf("x is even.\n");
	else
		printf("x is odd.\n");
}

把語句封裝成函數的基本步驟是:把語句放到函數體中,把變數改成函數的參數。這樣,以後要檢查一個數的奇偶性只需調用這個函數而不必重複寫這條語句了,例如:

print_parity(17);
print_parity(18);

if/else語句的語法規則如下:

語句 → if (控製表達式) 語句 else 語句

右邊的“語句”既可以是一條語句,也可以是由{}括起來的語句塊。一條if語句中包含一條子語句,一條if/else語句中包含兩條子語句,子語句可以是任何語句或語句塊,當然也可以是另外一條ifif/else語句。根據組合規則,ifif/else可以嵌套使用。例如可以這樣:

if (x > 0)
	printf("x is positive.\n");
else if (x < 0)
	printf("x is negative.\n");
else
	printf("x is zero.\n");

也可以這樣:

if (x > 0) {
	printf("x is positive.\n");
} else {
	if (x < 0)
		printf("x is negative.\n");
	else
		printf("x is zero.\n");
}

現在有一個問題,類似if (A) if (B) C; else D;形式的語句怎麼理解呢?可以理解成

if (A)
	if (B)
		C;
else
	D;

也可以理解成

if (A)
	if (B)
		C;
	else
		D;

第 1 節 “繼續Hello World”講過,C代碼的縮進只是為了程序員看起來方便,實際上對編譯器不起任何作用,你的代碼不管寫成上面哪一種縮進格式,在編譯器看起來都是一樣的。那麼編譯器到底按哪種方式理解呢?也就是說,else到底是和if (A)配對還是和if (B)配對?很多編程語言的語法都有這個問題,稱為Dangling-else問題。C語言規定,else總是和它上面最近的一個if配對,因此應該理解成elseif (B)配對,也就是按第二種方式理解。如果你寫成上面第一種縮進的格式就很危險了:你看到的是這樣,而編譯器理解的卻是那樣。如果你希望編譯器按第一種方式理解,應該明確加上{}:

if (A) {
	if (B)
		C;
} else
	D;

順便提一下,浮點型的精度有限,不適合用==運算符做精確比較。以下代碼可以說明問題:

double i = 20.0;
double j = i / 7.0;
if (j * 7.0 == i)
	printf("Equal.\n");
else
	printf("Unequal.\n");

不同平台的浮點數實現有很多不同之處,在我的平台上運行這段程序結果為Unequal,即使在你的平台上運行結果為Equal,你再把i改成其它值試試,總有些值會使得結果為Unequal。等學習了第 4 節 “浮點數”你就知道為什麼浮點型不能做精確比較了。

習題

1、寫兩個表達式,分別取整型變數x的個位和十位。

2、寫一個函數,參數是整型變數x,功能是打印x的個位和十位。