名稱空間
變體
操作

求值順序

來自 cppreference.com
< c‎ | 語言

任何 C 運算子的運算元的求值順序,包括函式呼叫表示式中函式引數的求值順序,以及任何表示式中子表示式的求值順序都是未指定的(除非下面另有說明)。編譯器將以任何順序求值,並且當再次求值相同的表示式時,可能會選擇另一種順序。

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

目錄

[編輯] 定義

[編輯] 求值

編譯器為每個表示式或子表示式執行兩種求值(兩者都是可選的)

  • 值計算:計算表示式返回的值。這可能涉及確定物件的身份(左值求值)或讀取先前分配給物件的值(右值求值)
  • 副作用:訪問(讀或寫)由volatile左值指定的物件,修改(寫入)物件、原子同步(C11 起),修改檔案,修改浮點環境(如果支援),或呼叫執行任何這些操作的函式。

如果表示式沒有產生副作用,並且編譯器可以確定該值未被使用,則該表示式可能不會被求值

[編輯] 排序

“sequenced-before”(先行)是同一執行緒中求值之間的一種非對稱、傳遞性的成對關係(如果涉及原子型別和記憶體屏障,它可能會跨執行緒擴充套件)。

  • 如果在子表示式 E1 和 E2 之間存在一個序列點,那麼 E1 的值計算和副作用都先行於 E2 的每個值計算和副作用
  • 如果求值 A 先行於求值 B,則 A 的求值將在 B 的求值開始之前完成。
  • 如果 A 不先行於 B 且 B 先行於 A,則 B 的求值將在 A 的求值開始之前完成。
  • 如果 A 不先行於 B 且 B 不先行於 A,則存在兩種可能性
    • A 和 B 的求值是無序的:它們可以以任何順序執行,並且可以重疊(在單個執行執行緒中,編譯器可以交錯構成 A 和 B 的 CPU 指令)
    • A 和 B 的求值是不可確定順序的:它們可以以任何順序執行,但不能重疊:A 將在 B 之前完成,或者 B 將在 A 之前完成。下次求值相同的表示式時,順序可能會相反。
(C11 起)

[編輯] 規則

1) 在所有函式引數和函式指示符的求值之後,以及在實際函式呼叫之前,存在一個序列點。
2) 在以下二元運算子的第一個(左)運算元求值之後,以及在第二個(右)運算元求值之前,存在一個序列點:&&(邏輯與)、||(邏輯或)和 ,(逗號)。
3) 在條件運算子 ?: 的第一個(左)運算元求值之後,以及在第二個或第三個運算元(以實際求值的為準)求值之前,存在一個序列點。
4) 在完整表示式(非子表示式的表示式:通常以分號或 if/switch/while/do控制語句結尾)求值之後,以及在下一個完整表示式之前,存在一個序列點。
5) 在完整宣告符的末尾存在一個序列點。
6) 在庫函式返回之前立即存在一個序列點。
7) 在格式化 I/O 中與每個轉換說明符關聯的操作之後存在一個序列點(特別是,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++ 文件,關於求值順序