星期三, 3月 23, 2011

C/C++: i = i++ + ++i;

這樣的一段 code,結果應該是什麼呢?

答案是「不一定」!

在 C/C++ 之中,operator + 並不是 sequence point。例如,運算式「f() + g()」之中,是 f() 先執行還是 g() 先執行,順序是不一定的(跟編譯器實做有關)。所以「i++ + ++i」之中,有可能 i++ 先作,亦有可能 ++i 先作。

並且,如「i=i++;」這樣的式子之中,i 被指派兩次。「i=」的指派動作可能發生在「i++」的指派動作之前、之後、或之間。

所以,為了節省 debug 耗用的腦細胞,任何程式設計師都不應該寫出這樣的程式碼。

====== 囧rz [分隔線] 囧rz ======

額外一提,「std::cout << f() << g();」這樣的程式碼之中,f() 與 g() 的執行順序也是不一定的。這是由於 C++ 的 operator overload 視為 function call。而 C++ 的 member function 實際上會被轉換成多傳入一個 this 引數的 global function。

例如(以下程式並非合法的 C++ code,只是為了解釋程式的行為):

std::cout << f() << g(); // 1
可能被轉成
std::cout.operator << (f()).operator << (g()); // 2
再被轉成
std::ostream::operator << (std::ostream::operator<< (std::cout, f()), g()); // 3

在上述 3 之中,是「std::ostream::operator<< (std::cout, f())」先執行還是「g()」先執行,順序也是不一定的。

所以,若 f() 與 g() 裡面都有輸出,則有可能先看到 g() 的輸出才看到 f() 的輸出,或是剛好相反。例如:

// 可能性 1:
tmp1 = f(); // 列印 f() 裡面的輸出
std::cout << tmp1; // 列印 f() 的結果
tmp2 = g(); // 列印 g() 裡面的輸出
std::cout << tmp2; // 列印 g() 的結果
// 可能性 2:
tmp1 = g(); // 列印 g() 裡面的輸出
tmp2 = f(); // 列印 f() 裡面的輸出
std::cout << tmp2; // 列印 f() 的結果
std::cout << tmp1; // 列印 g() 的結果

延伸閱讀:http://en.wikipedia.org/wiki/Sequence_point

2 則留言:

Unknown 提到...

所以,為了節省 debug 耗用的腦細胞,任何程式設計師都不應該寫出這樣的程式碼。

所以該怎麼寫~0.0
(上班時間打忙中~XD)

Tseng Victor 提到...

主要是要避免模糊不清的 sequence point。

以這個例子來說... 拆成三行就是了......