標籤:定義 eval 運算 volatil volatile 參數 最佳化 程式 out
《C++Primer5th》中文版第124頁C++語言沒有明確規定大多數二元運算子的求值順序,給編譯器最佳化留下了餘地。這種策略實際上是在代碼產生效率和程式潛在缺陷之間進行了權衡,這個是否可以接受?
1.首先可以知道優先順序規定了運算對象的組合方式,但是沒有說明運算對象按照什麼順序求值。
比如:
int i=f1()*f2();
在這裡雖然f1和f2在乘法之前被調用,但是f1先調用還是f2先調用卻不得而知。
2.再比如結合律:
--
int i=0;cout<<i<<" "<<+i<<endl;
結果可能是0 1或者是1 1.
因為雖然<<是左結合,但是對於那些沒有明確規定運算對象的求值順序的運算子而言,求值順序就和優先順序,以及結合律無關。
所以上面的式子是未定義的,即如果運算式指向並且修改了同一個對象,這樣的行為就是未定義的
- 邏輯與&&
- 邏輯非||
- 條件?:
-逗號,
上面四種運算子明確規定了運算對象的求值順序。
3.C++手冊
幾乎所有 C++ 運算子的求值順序(包括函數調用運算式中的函數參數求值順序和任何錶達式中子運算式的求值順序)都是未指定的。編譯器能以任何順序求值,並可以在再次求值相同運算式時選擇另一順序。
例子:
運算式 f1() + f2() + f3()
由於 operator+ 的從左至右結合性分析為 (f1() + f2()) + f3() ,但運行時對 f3 的函數調用可能首先、最後,或在 f1() 和 f2() 之間求值。
4.序列點規則(以下內容來自C++手冊)
--
序列點規則 (C++11 前)
定義
求值可能產生副效應:即訪問 volatile 左值所指代的對象、修改對象、調用庫 I/O 函數或調用做任何這些動作的函數。
序列點( sequence point )是執行序列中的點,在該點所有來自序列中先前求值的副效應均已完成,而後繼求值的副效應都未開始。
規則
1) 每個完整運算式結尾(典型地在分號)有一個序列點。
2) 調用函數時(無論該函數是否內聯,無論是否使用函數調用文法),所有函數參數的求值(若存在)後有一個序列點,它在函數體內的任何錶達式或語句執行前發生。
3) 複製函數傳回值後,和函數外任何語句的執行前有一個序列點。
4) 一旦函數執行開始,則在被調用函數完成前,不求值來自調用方函數的運算式(函數不能交錯)。
5) 每個使用內建(非重載)運算子的下列四種運算式的求值中,運算式 a 的求值後有一個序列點。
a && ba || ba ? b : ca , b
未定義行為
1) 前後序列點間,至多可以修改標量對象的儲存值一次,否則行為未定義。
i = ++i + i++; // 未定義行為i = i++ + 1; // 未定義行為( C++17 前)i = ++i + 1; // 未定義行為( C++11 前)++ ++i; // 未定義行為( C++11 前)f(++i, ++i); // 未定義行為( C++17 前)f(i = -1, i = -1); // 未定義行為( C++17 前)
2) 前後序列點間,訪問運算式求值所修改的標量對象的先前值,必須只為確定要儲存的值。若以任何其他方式訪問,則行為未定義。
cout << i << i++; // 未定義行為( C++17 前)a[i] = i++; // 未定義行為( C++17 前)
C++求值順序