本節書摘來自異步社群《c語言解惑》一書中的第1章,第1.3節,作者 傅道坤,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視
請問,下面這個程式的輸出是什麼?

輸出:
解惑1.3 邏輯操作符和增量操作符
初始值:x = 2, y=1, z=0
x = x&&y || z 按照操作符的優先級和關聯規則進行綁定。
x = (x&&y) || z
x = ((x&&y) || z)
(x = ((x&&y) || z))
(x = ((true&&true) || z)) 邏輯操作符的求值順序是從左到右。邏輯操作符的操作數如果是0,則解釋為false;如果是非零值,則解釋為true。
(x = (true || z)) 隻有兩個操作數都是true,邏輯與(&&)的結果才會是true;其他情況的求值結果都将是false。
(x = (true ||任意值)) 隻要有一個操作數是true,那麼不管另一個操作數是什麼,邏輯或(||)的結果都将是true。是以,我們不必再對這個表達式繼續求值了。
(x = true)
(x = 1)
1
再談define:這個程式裡的define語句與前一個程式裡的define語句有所不同:這個程式裡的“print”是一個帶參數的宏。在遇到帶參數的宏時,c語言的預處理器将分兩步進行替換:先把宏定義裡的形式參數替換為實際參數,再把宏調用替換為宏定義體。
在這個程式裡,“print”宏有一個形式參數int。“print<code>(x)”是使用實際參數“x”進行的“print”調用。在擴充“print”調用的時候,c語言預處理器會先把宏定義裡的所有“int”替換為“x”,再把宏調用“print</code>(x)”替換為結果字元串“printf``("%dn", x)”。請注意,形式參數int并不是比對單詞“printf”裡的“int”字元的。這是因為宏定義裡的形式參數是辨別符——具體到這個例子,形式參數int隻對辨別符int進行比對和替換。
初始值:x = 1, y=1, z=0
x || ! y&&z
x || (! y)&&z 對操作符和操作數進行綁定。
x || ((! y)&&z)
(x || ((! y)&&z))
(true || ((! y)&&z)) 按從左到右的順序求值。
(true ||任意值)
true,即1
初始值:x = 1,y=1
z = x ++ - 1
z = (x ++) – 1 對操作符和操作數進行綁定。
z = ((x ++) - 1)
(z = ((x ++) - 1))
(z = (1 - 1)),此時x=2 出現在操作數右邊的“++”操作符是所謂的“後”遞增操作符:先在表達式裡用該操作數完成有關的計算,再對它進行遞增。
(z = 0)
z += - x ++ + ++ y
z += - (x ++) + (++ y) 一進制操作符的關聯是從右到左,因而“++”操作符将先于一進制操作符“-”得到綁定。(事實上,如果先去綁定“-”操作符的話,這個表達式将無法成立。這是因為“++”和“--”操作符都要求以一個變量(更準确地說,以一個“左值”[1])作為其操作數。“x”是一個左值,但“-x”不是。)
z += (- (x ++)) + (++ y)
z += ((- (x ++)) + (++ y))
(z += ((- (x ++)) + (++ y)))
(z += ((-2) + 2)),此時x=3, y=2
(z += 0)
(z = 0 + 0)
按照從内到外的順序依次求值。
關于記号:計算機程式的源代碼文本是由一系列記号構成的,而編譯一個程式的第一項工作就是把那些記号一個一個地分解開。這在絕大多數場合都沒有什麼困難,但有些字元序列可能會讓人感到困惑。比如說,如果上面這個例子裡的表達式有一部分根本沒有空格——就像下面這樣:
x+++++y
那麼,這個沒有空格的表達式與有空格的表達式還會是等價的嗎?
為避免産生二義性,如果一個字元串能夠解釋為多個操作符,c語言編譯器将按照“構成操作符的字元個數越多越好”的原則來作出選擇。根據這一原則,“x+++++y”解釋為:
x++ ++ + y
而這已經不是一個合法的表達式了。
初始值:x = 3,z=0
z = x / ++ x
z = x / (++ x)
z = (x / (++ x))
(z = (x / (++ x)))
如果你還像以前那樣按照從内到外的順序對這個表達式進行求值,即先對x進行遞增,然後作為除數、用x作被除數去進行除法計算。問題是:作為被除數的x到底是幾?是3還是4?換個問法,被除數到底是遞增前的x值,還是遞增後的x值?請注意,c語言并沒有對這種“副作用”作出明确的規定,而是由c編譯器的編寫者決定的[2]。這個例子的教訓是:如果你無法斷定會不會産生副作用,那麼就盡量不要寫這樣的表達式。