c++ 中 `++i` 與 `i++` 在運算表示式中的優先順序
阿新 • • 發佈:2018-12-31
在算術表示式中,優先順序高的運算子先運算,優先順序低的運算子後運算,不同的優先順序直接影響表示式的計算結果。
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 = 10vs2012
i = 6
j = 12
計算過程:
gcc
- 從左到右:fun = (i++) + (i++) = 3 + 3 = 6
- j = fun + (++i),表示式中有(++i)
- 先計算(++i): i = 4
- j = fun + i = fun + 4 = 10
- 計算左邊(i++): i = 5
- 計算中間(i++): i = 6
vs2012
- 表示式中有(++i),先計算 :i = 4
- 從左到右:fun = (i++) + (i++) = i + i = 4 + 4 = 8
- j = fun + i = 8 + 4 = 12
- 計算左邊(i++): i = 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 = 16vs2012
i = 6
j = 18
計算過程:
gcc
- 從左到右:fun = (++i) + (++i)
- 先計算左側(++i):i = 4
- 再計算右側(++i):i = 5
- 然後計算 :fun = i + i = 5 + 5 = 10
- j = fun + (++i),
- 先計算右邊(++i): i = 6
- 然後計算 :j = fun + i = 10 + 6 = 16
- 從左到右:fun = (++i) + (++i)
vs2012
- 表示式中有(++i),先計算
- 先計算左側(++i):i = 4
- 再計算中間(++i):i = 5
- 先計算右邊(++i): i = 6
- 從左到右:fun = i + i = 6 + 6 = 12
- j = fun + i = 12 + 6 = 18
- 表示式中有(++i),先計算
4. 例項3
計算如下表達式的值:
int i = 3, j = 0;
j = (++i) + (++i) + (++i) + (++i);
cout << "i = " << i << endl;
cout << "j = " << j << endl;
結果:
gcc
i = 7
j = 23vs2012
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 = 0
,i < N
,y = i++
分開。
c++ 標準只保證,在一個順序點處,求值和副作用全部完成,然後才能進入下面的部份,但是在兩個順序點直接,副作用的順序並沒有規定。所以在多個副作用同時存在的情況下,不同的編譯器會有不同的執行順序。
實際工作中,完全可以通過引入中間變數,避開“順序點”這樣容易出錯,也極大地降低程式碼可讀性的“邊緣概念”。
6. c 與 c++ 順序點出現的位置
- && (邏輯與)、 || (邏輯或)、逗號操作符的左運算元與右運算元求值之間(前兩者是短路求值的一部分)。例如,表示式*p++ != 0 && *q++ != 0,子表示式*p++ != 0的副作用都會在試圖訪問q之前完成。
- 三元條件運算子的第一個運算元之後,第二或第三運算元之前。例如,表示式a = (*p++) ? (*p++) : 0在第一個*p++之後存在順序點,因而在第二個*p++求值之前已經做完一次自增。
- 完整表示式結束處。包括表示式語句(如賦值a=b;),返回語句,if、switch、while、do-while語句的控制表示式,for語句的3個表示式。
- 函式呼叫時的函式入口點。函式實參的求值順序未指定,但數序點意味著這些實參求值的副作用在進入函式時都已經完成。表示式f(i++) + g(j++) + h(k++),呼叫f(), g(), h()的順序未指定,i, j, k的自增順序也未指定。函式呼叫f(a,b,c)的實參列表不是逗號運算子,a, b, and c的求值順序未指定。
- 函式返回時,在返回值已經複製到呼叫上下文。(僅C++標準指出這一順序點)
- 初始化的結束。例如,宣告int a = 5;中的對5求值之後。
- 在宣告序列的每個宣告(declarator)之間。例如,int x = a++, y = a++的兩次a++求值之間。注意,此例不是逗號運算子。
7. 參考
參見維基百科