運算子、優先順序與結合性
運算子是對值執行動作的符號——做加法的 +、做比較的 ==、做指派的 =。就像在普通算術中你學到先做乘法再做加法,C 也有規則決定當一個運算式裡出現多個運算子時,哪一個先動作。這些規則就是優先順序(誰先)與結合性(同級運算子的決勝規則:由左到右或由右到左)。
C 有一套依工作分類的豐富運算子。算術運算子(+ - * / 以及取餘數的 %)計算數值。關係運算子(< > <= >= == !=)做比較並產生 0 或 1。邏輯運算子(&& 表且、|| 表或、! 表非)結合真假值且會短路,意思是 && 在左側為假時停止、|| 在左側為真時停止。指派(= 以及複合形式如 += -=)儲存一個值、自身也產生那個值。逗號運算子先求值它的左側並丟棄,再求值並產生它的右側。優先順序決定分組:在 a + b * c 中乘法結合得較緊,所以是 a + (b * c)。結合性打破平手:a - b - c 是左結合,意思是 (a - b) - c,而指派是右結合,所以 a = b = c 意思是 a = (b = c)。
為什麼重要:誤判優先順序是經典的錯誤來源,因為程式碼能編譯卻算出了錯誤的分組。誠實的建議不是去背整張十五層的表,而是只要運算式不明顯就加上括號——它們在執行期毫無成本,卻讓意圖無可置疑。另外請注意:對多數運算子,C 並未承諾兩個運算元的求值順序;依賴那個順序是未指定行為。
int r = 2 + 3 * 4; /* 14,不是 20:* 比 + 結合得緊 */ int s = (2 + 3) * 4; /* 20:括號強制分組 */ int a, b, c; a = b = c = 0; /* 右結合:a = (b = (c = 0)) */
優先順序讓 * 比 + 先分組;括號可覆寫它。指派由右往左結合,所以連鎖的 = 從右邊讀起。
別依賴運算元的求值順序——對多數運算子它是未指定的,所以像 f() + g() 可能以任一順序呼叫它們。對分組有疑慮時就加括號;它們免費且消除歧義。