1. 程式人生 > >C++求值順序

C++求值順序

|| Language 運算 ren c++ 一次 但是 並且 表達

《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 && b
a || b
a ? b : c
a , 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++求值順序