C/C++中++符號的運算順序是怎樣的?

為什麼用不同的編譯器會出現不同的結果?在這兩種結果中,程序是按什麼順序運行的?對於++這個符號,是否有統一的標準?


@vczh說的方法確實能幹掉未定義行為,但並沒有給出任何萬全的保證。

首先關於++的所謂「運算順序」,可以參考我的另一個答案:

關於Sequence point與Undefined behaviour之間的關係? - 丁冬的回答

輪子哥所說的方法可以提供的保證是所有副作用行為之間都不是unsequenced,但充其量只能保證indeterminately sequenced,也就是說可以保證的是不會發生求值行為的重疊,但求值順序還是沒有保證的:

比如:

Integer i = 0;
Integer j = i-- + ++i;

由於運算符重載,後置--、前置++、+都是被當作函數參與決議的。i--和++i就成了operator +的兩個實參表達式,這兩個表達式的求值行為由於分別又是函數調用,所以被提升為indeterminately sequenced,但到底哪個先被求值還是未明確的。所以:

1.如果i--先被求值,則i--返回值0,++i返回值0,j被初始化為0;

2.如果++i先被求值,則i--返回值1,++i返回值1,j被初始化為2。

雖然說i不是scalar object,確實不會發生未定義行為,但未明確行為仍然被觸發了,所以結果還是沒有保證。


我也來補充個簡化版吧:

這個不是優先順序的問題也不是運算順序的問題,更不是加個括弧能解決的問題,更跟什麼進棧出棧壓棧順序半毛錢關係都沒有,這是序列點的問題:

假設有兩個返回bool的函數:

bool A();

bool B();

又有一個C函數:

void C( bool x , bool y );

那麼調用:

C( A() , B() );

時,請問是先執行A還是先執行B呢?

答案是不知道。處於兩個序列點之間的操作,先後順序是未指定的,即對順序沒有任何保證。 這樣做的目的是可以方便編譯器想怎麼整怎麼整。

所以處於同一個序列點內的所有的調用你都不能假定以什麼順序發生。


http://blog.csdn.net/xtlisk/article/details/40116813


這個答案主要是在反對 vczh 的答案

vczh的做法的確保證了 在函數調用之後 副作用完成

但是 僅僅保證副作用完成是不夠的

這個UB 還包括了 表達式求值順序

對於 cout &<&< i++ &<&< ++i

轉換一下格式 這個函數調用是這個樣子的

operator&<&<( operator&<&<(cout, operator++(i, 0)), operator++(i) )

調用這個函數的時候 會對參數進行求值

而對於這2個參數哪一個先求值是不受限制的

可以是第一個參數 operator&<&<(cout, operator++(i, 0)) 先求值 結果是 02

也可以是第二個參數 operator++(i) 先求值 結果是 11

重載++實現原來的功能 並不能避免這個++導致的UB

貼圖打臉

這是按照vczh答案的說明 寫出來的代碼

各種細節就不必深究了 骨架上是這樣就行了

#include &
using namespace std;

struct Integer
{
private:
int n;
public:
explicit Integer(int);
Integer operator++(int);
Integer operator++();

friend ostream operator&<&<(ostream, const Integer); }; Integer::Integer(int m) { n = m; } Integer Integer::operator++(int) { return Integer(n++); } Integer Integer::operator++() { //如果這裡返回的是*this的引用 將會導致更加複雜的UB 和題意無關 暫略 return Integer(++n); } ostream operator&<&<(ostream out, const Integer tmp) { return out &<&< tmp.n; } int main() { Integer i(0); cout &<&< i++ &<&< ++i &<&< endl; return 0; }

這是在不同編譯器上面的運行結果

http://ideone.com/eRyLDg 這個結果是02

Ideone.com - bXOsi0 這個結果是11

本來只是在評論裡面說明一下 沒想到 vczh 刪評論+屏蔽

只好另開答案 反駁 以正視聽

vczh先生 對於UB 不了解請不要強答 丟人了

正確的做法是避開它

請不要正面入坑 這不是勇氣與智慧

另外 我反對就反對 你開不開心 我毫不在意

最後 回答一下題主

恩 運算順序沒有統一的標準

除了 || , ?: 這幾個運算符要求從左向右求值之外

其他任何運算符都沒限制求值順序

這種結果依賴運算順序的代碼是錯誤的代碼


別糾結了。

工程上直接用括弧和分拆句子來規避這些噁心的優先順序差異性問題。

不優美,但不出錯


題主我覺得我可以帶壞你一下。如果你一定要有個確定的順序,你就自己寫一個叫Integer的struct,裡面放一個int,實現兩個operator++和到ostream和istream的operator&<&<和operator&>&>。然後用自己的Integer類型代替int,保證以後所有++到了任何編譯器上的效果都完全一致,毫無UB。

====以下為無關內容====

有人說我拉黑了。其實我這個人就這樣,你說我不對沒關係,你對我不尊敬我就拉黑。你看像 @空明流轉天天跳出來反對我,我跟他關係還是很好的。所以我都是全看你語氣的。只要你能反對我反對的讓我開心,像某冬,人家語氣就很好,那我就會接受,哈哈哈哈哈哈哈。

這些亂七八糟的知識我都知道,但是反正我已經聲明了我來知乎就是來灌水了,所以我從來不在乎我說的東西是否像數學語言般精確,那是看我的答案的人應該自己去弄懂的,你又沒有給我錢。


1.關於++的行為,這個是有標準的。標準的內容就是++在前先加後取,++在後先取後加。

2.關於這個「不同」,並不是++的不同,而是cout里&<&<連續的表達式里結合順序的差異。可以看出來g++/clang++是從左至右結合的;cl里是從右至左取值結合的。如果我沒記錯,按照標準,operator&>&>理應是從左至右結合的。

那麼問題來了,為什麼vs里的cl不同?說好的標準化呢……?


你這個例子中,不是++的執行順序不同;而是&>&>的執行順序不同。


你的代碼寫的有歧義,編譯順序就是看編譯器的

不是指前++跟後++的編譯順序

建議你多用些中間變數 消除歧義

最終還是得多看書 這些問題書里都有寫


推薦閱讀:

C 語言的內存管理如何比 C++ 的 RAII 靠譜?
輸出測試時有哪些有趣的字元串可以用來代替「hello, world"?
有些語言用{},有些語言用end,大家怎麼看?
為什麼unordered_map源碼中的need_rehash函數只是將桶容量擴到下一個最小的素數?
C++里為什麼要添加lambda?是C++本身的什麼問題造成的?

TAG:編程語言 | 編程 | CC |