1. if語句

目前我們寫的簡單函數中可以有多條語句,但這些語句總是從前到後順序執行的。除了順序執行之外,有時候我們需要檢查一個條件,然後根據檢查的結果執行不同的後續代碼,在C語言中可以用分支語句(Selection Statement)實現,比如:

if (x != 0) {
	printf("x is nonzero.\n");
}

其中x != 0表示“x不等於0”的條件,這個表達式稱為控製表達式(Controlling Expression)如果條件成立,則{}中的語句被執行,否則{}中的語句不執行,直接跳到}後面。if和控製表達式改變了程序的控制流程(Control Flow),不再是從前到後順序執行,而是根據不同的條件執行不同的語句,這種控制流程稱為分支(Branch)。上例中的!=號表示“不等於”,像這樣的運算符有:

表 4.1. 關係運算符和相等性運算符

運算符含義
==等於
!=不等於
>大於
<小於
>=大於或等於
<=小於或等於

注意以下幾點:

  1. 這裡的==表示數學中的相等關係,相當於數學中的=號,初學者常犯的錯誤是在控製表達式中把==寫成=,在C語言中=號是賦值運算符,兩者的含義完全不同。

  2. 如果表達式所表示的比較關係成立則值為真(True),否則為假(False),在C語言中分別用int型的1和0表示。如果變數x的值是-1,那麼x>0這個表達式的值為0,x>-2這個表達式的值為1。

  3. 在數學中a<b<c表示b既大於a又小於c,但作為C語言表達式卻不是這樣。以上幾種運算符都是左結合的,請讀者想一下這個表達式應如何求值。

  4. 這些運算符的兩個操作數應該是相同類型的,兩邊都是整型或者都是浮點型可以做比較,但兩個字元串不能做比較,在第 1.5 節 “比較字元串”我們會介紹比較字元串的方法。

  5. ==和!=稱為相等性運算符(Equality Operator),其餘四個稱為關係運算符(Relational Operator),相等性運算符的優先順序低於關係運算符。

總結一下,if (x != 0) { ... }這個語句的計算順序是:首先求x != 0這個表達式的值,如果值為0,就跳過{}中的語句直接執行後面的語句,如果值為1,就先執行{}中的語句,然後再執行後面的語句。事實上控製表達式取任何非0值都表示真值,例如if (x) { ... }if (x != 0) { ... }是等價的,如果x的值是2,則x != 0的值是1,但對於if來說不管是2還是1都表示真值。

if語句相關的語法規則如下:

語句 → if (控製表達式) 語句
語句 → { 語句列表 }
語句 → ;

在C語言中,任何允許出現語句的地方既可以是由;號結尾的一條語句,也可以是由{}括起來的若干條語句或聲明組成的語句塊(Statement Block),語句塊和上一章介紹的函數體的語法相同。注意語句塊的}後面不需要加;號。如果}後面加了;號,則這個;號本身又是一條新的語句了,在C語言中一個單獨的;號表示一條空語句(Null Statement)。上例的語句塊中只有一條語句,其實沒必要寫成語句塊,可以簡單地寫成:

if (x != 0)
	printf("x is nonzero.\n");

語句塊中也可以定義局部變數,例如:

void foo(void)
{
	int i = 0;
	{
		int i = 1;
		int j = 2;
		printf("i=%d, j=%d\n", i, j);
	}
	printf("i=%d\n", i); /* cannot access j here */
}

和函數的局部變數同樣道理,每次進入語句塊時為變數j分配存儲空間,每次退出語句塊時釋放變數j的存儲空間。語句塊也構成一個作用域,和例 3.6 “作用域”的分析類似,如果整個源檔案是一張大紙,foo函數是蓋在上面的一張小紙,則函數中的語句塊是蓋在小紙上面的一張更小的紙。語句塊中的變數i和函數的局部變數i是兩個不同的變數,因此兩次打印的i值是不同的;語句塊中的變數j在退出語句塊之後就沒有了,因此最後一行的printf不能打印變數j,否則編譯器會報錯。語句塊可以用在任何允許出現語句的地方,不一定非得用在if語句中,單獨使用語句塊通常是為了定義一些比函數的局部變數更“局部”的變數。

習題

1、以下程序段編譯能通過,執行也不出錯,但是執行結果不正確(根據第 3 節 “程序的調試”的定義,這是一個語義錯誤),請分析一下哪裡錯了。還有,既然錯了為什麼編譯能通過呢?

int x = -1;
if (x > 0);
	printf("x is positive.\n");