命名空間
變體
動作

求值順序

出自 cppreference.com
< c‎ | 語言

任何 C 運算子運算元的求值順序,包含函式呼叫表達式中函式引數的求值順序,以及任何表達式內子表達式的求值順序,皆為未指定(除下文註明者外)。編譯器可以任何順序對其求值,且在再次對相同表達式求值時,亦可選擇不同的順序。

C 語言中沒有「從左至右」或「從右至左」求值的概念,這不應與運算子的「從左至右」與「從右至左」結合性混淆:表達式 f1() + f2() + f3() 由於 + 運算子的從左至右結合性,會被解析為 (f1() + f2()) + f3(),但在執行階段,對 f3 的函式呼叫可能會在最先、最後,或在 f1()f2() 之間被求值。

目錄

[編輯] 定義

[編輯] 求值

對於每個表達式或子表達式,編譯器會執行兩種求值(兩者皆為選擇性):

  • 值計算 (value computation):計算表達式所回傳的值。這可能涉及確定物件的識別(左值求值)或讀取先前指派給物件的值(右值求值)。
  • 副作用 (side effect):存取(讀取或寫入)由 volatile 左值所指定的物件、修改(寫入)物件、原子同步(自 C11 起)、修改檔案、修改浮點環境(若支援),或呼叫執行上述任一操作的函式。

若表達式未產生副作用,且編譯器能判斷該值未被使用,則該表達式可能不會被求值

[編輯] 排序

「順序先於」(sequenced-before) 是同一執行緒內求值之間的一種非對稱、可遞移的兩兩關係(若涉及原子型別與記憶體屏障,此關係可跨越執行緒)。

  • 若在子表達式 E1 與 E2 之間存在順序點 (sequence point),則 E1 的所有值計算與副作用皆 順序先於 (sequenced-before) E2 的所有值計算與副作用。
  • 若求值 A 順序先於求值 B,則 A 的求值會在 B 的求值開始前完成。
  • 若 A 未順序先於 B 且 B 順序先於 A,則 B 的求值會在 A 的求值開始前完成。
  • 若 A 未順序先於 B 且 B 也未順序先於 A,則存在兩種可能性:
    • A 與 B 的求值是「未排序的」(unsequenced):它們可以任何順序執行且可能重疊(在單一執行緒中,編譯器可能交錯構成 A 與 B 的 CPU 指令)。
    • A 與 B 的求值是「不確定順序的」(indeterminably-sequenced):它們可以任何順序執行但不可重疊:要麼 A 在 B 前完成,要麼 B 在 A 前完成。下次對相同表達式求值時,順序可能相反。
(自 C11 起)

[編輯] 規則

1) 在所有函式引數及函式指示項求值後,且在實際函式呼叫之前,存在一個順序點。
2) 在下列二元運算子的第一個(左)運算元求值後,且在第二個(右)運算元求值前,存在一個順序點:&&(邏輯 AND)、||(邏輯 OR)及 ,(逗號)。
3) 在條件運算子 ?: 的第一個(左)運算元求值後,且在第二個或第三個(視何者被求值而定)運算元求值前,存在一個順序點。
4) 在完整表達式(不是子表達式的表達式:通常是以分號結尾,或為 if/switch/while/do控制語句)求值後,且在下一個完整表達式之前,存在一個順序點。
5) 在完整宣告子 (full declarator) 的末尾存在一個順序點。
6) 在函式庫函式回傳前,立即存在一個順序點。
7) 在格式化 I/O 中,與每個轉換指定符 (conversion specifier) 相關聯的動作之後存在一個順序點(特別是,scanf 將不同欄位寫入同一個變數,以及 printf 使用 %n 對同一個變數進行讀取與修改或多次修改,皆是符合規範的)。
8) 在函式庫函式 qsortbsearch 所進行的每次比較函式呼叫前後,以及在 qsort 進行比較函式呼叫與移動相關物件之間,皆存在順序點。
(自 C99 起)
9) 任何運算子的運算元之值計算(而非副作用)皆順序先於該運算結果的值計算(而非其副作用)。
10) 直接指派運算子與所有複合指派運算子的副作用(左引數的修改)順序後於兩個引數的值計算(而非副作用)。
11) 後遞增與後遞減運算子的值計算順序先於其副作用。
12) 若一個函式呼叫未順序先於或順序後於另一個函式呼叫,則它們是不確定順序的(構成不同函式呼叫的 CPU 指令無法交錯,即使函式已被內聯)。
13)初始化列表表達式中,所有求值皆為不確定順序的。
14) 相對於一個不確定順序的函式呼叫,複合指派運算子的操作,以及前置與後置形式的遞增與遞減運算子,皆視為單一求值。
(自 C11 起)

[編輯] 未定義行為

1) 若對純量物件的副作用相對於對同一個純量物件的另一個副作用是未排序的,則行為是未定義的
i = ++i + i++; // undefined behavior
i = i++ + 1; // undefined behavior
f(++i, ++i); // undefined behavior
f(i = -1, i = -1); // undefined behavior
2) 若對純量物件的副作用相對於使用相同純量物件值的數值計算是未排序的,則行為是未定義的。
f(i, i++); // undefined behavior
a[i] = i++; // undefined bevahior
3) 只要至少存在一種允許的子表達式求值順序會產生此類未排序的副作用,上述規則即適用。

[編輯] 參見

運算子優先順序:定義了表達式如何從其原始程式碼表示中建構。

C++ 文件(關於 求值順序
English Deutsch 日本語 中文(简体) 中文(繁體)