求值順序
來自 cppreference.com
任何 C 運算子的運算元的求值順序,包括函式呼叫表示式中函式引數的求值順序,以及任何表示式中子表示式的求值順序都是未指定的(除非下面另有說明)。編譯器將以任何順序求值,並且當再次求值相同的表示式時,可能會選擇另一種順序。
在 C 語言中沒有從左到右或從右到左的求值概念,這不應與運算子的從左到右和從右到左的結合性混淆:表示式 f1() + f2() + f3()
由於運算子+的從左到右結合性而被解析為 (f1() + f2()) + f3()
,但在執行時,對 f3
的函式呼叫可能首先、最後,或在 f1()
或 f2()
之間進行求值。
目錄 |
[編輯] 定義
[編輯] 求值
編譯器為每個表示式或子表示式執行兩種求值(兩者都是可選的)
- 值計算:計算表示式返回的值。這可能涉及確定物件的身份(左值求值)或讀取先前分配給物件的值(右值求值)
- 副作用:訪問(讀或寫)由volatile左值指定的物件,修改(寫入)物件、原子同步(C11 起),修改檔案,修改浮點環境(如果支援),或呼叫執行任何這些操作的函式。
如果表示式沒有產生副作用,並且編譯器可以確定該值未被使用,則該表示式可能不會被求值。
[編輯] 排序
“sequenced-before”(先行)是同一執行緒中求值之間的一種非對稱、傳遞性的成對關係(如果涉及原子型別和記憶體屏障,它可能會跨執行緒擴充套件)。
- 如果在子表示式 E1 和 E2 之間存在一個序列點,那麼 E1 的值計算和副作用都先行於 E2 的每個值計算和副作用
|
(C11 起) |
[編輯] 規則
1) 在所有函式引數和函式指示符的求值之後,以及在實際函式呼叫之前,存在一個序列點。
2) 在以下二元運算子的第一個(左)運算元求值之後,以及在第二個(右)運算元求值之前,存在一個序列點:
&&
(邏輯與)、||
(邏輯或)和 ,
(逗號)。3) 在條件運算子
?:
的第一個(左)運算元求值之後,以及在第二個或第三個運算元(以實際求值的為準)求值之前,存在一個序列點。
5) 在完整宣告符的末尾存在一個序列點。
6) 在庫函式返回之前立即存在一個序列點。
|
(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++ 文件,關於求值順序
|