目前我們寫的簡單函數中可以有多條語句,但這些語句總是從前到後順序執行的。除了順序執行之外,有時候我們需要檢查一個條件,然後根據檢查的結果執行不同的後續代碼,在C語言中可以用分支語句(Selection Statement)實現,比如:
if (x != 0) { printf("x is nonzero.\n"); }
其中x != 0
表示“x不等於0”的條件,這個表達式稱為控製表達式(Controlling Expression)如果條件成立,則{}中的語句被執行,否則{}中的語句不執行,直接跳到}後面。if
和控製表達式改變了程序的控制流程(Control Flow),不再是從前到後順序執行,而是根據不同的條件執行不同的語句,這種控制流程稱為分支(Branch)。上例中的!=號表示“不等於”,像這樣的運算符有:
注意以下幾點:
這裡的==表示數學中的相等關係,相當於數學中的=號,初學者常犯的錯誤是在控製表達式中把==寫成=,在C語言中=號是賦值運算符,兩者的含義完全不同。
如果表達式所表示的比較關係成立則值為真(True),否則為假(False),在C語言中分別用int
型的1和0表示。如果變數x
的值是-1,那麼x>0
這個表達式的值為0,x>-2
這個表達式的值為1。
在數學中a<b<c
表示b
既大於a
又小於c
,但作為C語言表達式卻不是這樣。以上幾種運算符都是左結合的,請讀者想一下這個表達式應如何求值。
這些運算符的兩個操作數應該是相同類型的,兩邊都是整型或者都是浮點型可以做比較,但兩個字元串不能做比較,在第 1.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");