1. 程式人生 > >c++ 中 `++i` 與 `i++` 在運算表示式中的優先順序

c++ 中 `++i` 與 `i++` 在運算表示式中的優先順序

在算術表示式中,優先順序高的運算子先運算,優先順序低的運算子後運算,不同的優先順序直接影響表示式的計算結果。

1. 說明

  • gcc中
    gcc中的加法運算表示式中,是按照從左到右按順序,如果運算子兩邊有++i運算元,就先進行++i操作,然後進行加法運算;

  • vs中
    vs中的加法運算表示式中,則不一樣,只要表示式中有++i運算元,就要先計算,最後才是進行加法運算。

加法運算可以擴充套件到減法、乘法、除法運算和前置–、後置–。但是如果是四則混合運算還要考慮加、減、乘、除的優先順序問題。

2. 例項1

計算如下表達式的值:

int i = 3, j = 0;
j = (i++) + (i++) + (++i);
cout
<< "i = " << i << endl; cout << "j = " << j << endl;

結果:

  • gcc
    i = 6
    j = 10

  • vs2012
    i = 6
    j = 12

計算過程:

  • gcc

    1. 從左到右:fun = (i++) + (i++) = 3 + 3 = 6
    2. j = fun + (++i),表示式中有(++i)
      1. 先計算(++i): i = 4
      2. j = fun + i = fun + 4 = 10
    3. 計算左邊(i++): i = 5
    4. 計算中間(i++): i = 6
  • vs2012

    1. 表示式中有(++i),先計算 :i = 4
    2. 從左到右:fun = (i++) + (i++) = i + i = 4 + 4 = 8
    3. j = fun + i = 8 + 4 = 12
    4. 計算左邊(i++): i = 5
    5. 計算中間(i++): i = 6

3. 例項2

計算如下表達式的值:

int i = 3, j = 0;
j = (++i) + (++i) + (++i);
cout << "i = " << i << endl;
cout << "j = " << j << endl;

結果:

  • gcc
    i = 6
    j = 16

  • vs2012
    i = 6
    j = 18

計算過程:

  • gcc

    1. 從左到右:fun = (++i) + (++i)
      1. 先計算左側(++i):i = 4
      2. 再計算右側(++i):i = 5
      3. 然後計算 :fun = i + i = 5 + 5 = 10
    2. j = fun + (++i),
      1. 先計算右邊(++i): i = 6
      2. 然後計算 :j = fun + i = 10 + 6 = 16
  • vs2012

    1. 表示式中有(++i),先計算
      1. 先計算左側(++i):i = 4
      2. 再計算中間(++i):i = 5
      3. 先計算右邊(++i): i = 6
    2. 從左到右:fun = i + i = 6 + 6 = 12
    3. j = fun + i = 12 + 6 = 18

4. 例項3

計算如下表達式的值:

int i = 3, j = 0;
j = (++i) + (++i) + (++i) + (++i);
cout << "i = " << i << endl;
cout << "j = " << j << endl;

結果:

  • gcc
    i = 7
    j = 23

  • vs2012
    i = 7
    j = 28

計算過程與例項 1 和例項 2 類似,就不再推導。

5. 理解

為什麼同是 c++,同樣的程式碼在 gcc 下和 vs 下的結果會不一樣呢?下面,先從幾個概念說起:

  • 表示式的副作用

    • 含義:表示式在求值過程中要改變該表示式中作為運算元的某個變數的值。
    • 原因:表示式中包含了具有副作用的操作符,這樣的操作符包括:賦值操作符、複合賦值操作符、增1減1操作符。如:j = (i++) + i + (i++)
  • 順序點

    • 含義:也稱作序列點,是計算機程式中一些執行點,在該點處之前的求值的所有的副作用已經發生,在它之後的求值的所有副作用仍未開始
    • 例子:for(int i = 0; i < N; y = i++)中的兩個分號就是順序點,把int i = 0i < Ny = i++分開。

c++ 標準只保證,在一個順序點處,求值和副作用全部完成,然後才能進入下面的部份,但是在兩個順序點直接,副作用的順序並沒有規定。所以在多個副作用同時存在的情況下,不同的編譯器會有不同的執行順序。

實際工作中,完全可以通過引入中間變數,避開“順序點”這樣容易出錯,也極大地降低程式碼可讀性的“邊緣概念”。

6. c 與 c++ 順序點出現的位置

  1. && (邏輯與)、 || (邏輯或)、逗號操作符的左運算元與右運算元求值之間(前兩者是短路求值的一部分)。例如,表示式*p++ != 0 && *q++ != 0,子表示式*p++ != 0的副作用都會在試圖訪問q之前完成。
  2. 三元條件運算子的第一個運算元之後,第二或第三運算元之前。例如,表示式a = (*p++) ? (*p++) : 0在第一個*p++之後存在順序點,因而在第二個*p++求值之前已經做完一次自增。
  3. 完整表示式結束處。包括表示式語句(如賦值a=b;),返回語句,if、switch、while、do-while語句的控制表示式,for語句的3個表示式。
  4. 函式呼叫時的函式入口點。函式實參的求值順序未指定,但數序點意味著這些實參求值的副作用在進入函式時都已經完成。表示式f(i++) + g(j++) + h(k++),呼叫f(), g(), h()的順序未指定,i, j, k的自增順序也未指定。函式呼叫f(a,b,c)的實參列表不是逗號運算子,a, b, and c的求值順序未指定。
  5. 函式返回時,在返回值已經複製到呼叫上下文。(僅C++標準指出這一順序點)
  6. 初始化的結束。例如,宣告int a = 5;中的對5求值之後。
  7. 在宣告序列的每個宣告(declarator)之間。例如,int x = a++, y = a++的兩次a++求值之間。注意,此例不是逗號運算子。

7. 參考

參見維基百科