2013-07-04 44 views
12

交換兩個整型變量,而不是使用臨時存儲的涉嫌「聰明」(但實際上效率低下)的方式,往往涉及到這一行: 表達式a^= b^= a^= b中是否有序列點,還是未定義?

int a = 10; 
int b = 42; 

a ^= b ^= a ^= b; /*Here*/ 

printf("a=%d, b=%d\n", a, b); 

但我想知道,複合賦值操作符,如^=是不是序列點,是嗎? 這是否意味着它實際上是未定義的行爲?

+2

如果您編寫的代碼很難說明發生了什麼問問自己是否有更直接的方式讓未來的開發人員理解? –

+1

請注意,如果您已經在C++代碼中看到了這一點,那麼C++對賦值運算符有不同的規則,它允許在C中未定義的某些構造(我不確定這個構造)。 – hvd

+3

[Sequence Point - Xor在數組上交換得到錯誤的結果](http://stackoverflow.com/questions/9958514/sequence-point-xor-swap-on-array-get-wrong-result) –

回答

17
a ^= b ^= a ^= b; /*Here*/ 

這是未定義的行爲。

您在兩個序列點之間多次修改對象(a)。

(C99,6.5p2)「前一個和下一個順序點之間的對象應具有其存儲的值在由表達式的評估改性最多一次。

簡單分配以及化合物分配不引入序列點。在這裏有表達的語句表達前後表達語句後的序列點。

序列點在C99的附錄C(信息)和C11標準市場上市

13

^=不序列點,是他們

他們不是。

這是否意味着它實際上是未定義的行爲?

是的。不要使用這種「聰明」的技巧。

+0

感謝您的確認。別擔心,我從來沒有打算首先使用它。 – Medinoc

7

該表達式中沒有序列點,因此它會產生未定義的行爲。

你可以平凡解決這個問題,通過使用逗號運算符,它引入序列點保留大部分簡潔的:

a ^= b, b ^= a, a ^= b; 
+1

此時你可以把它放在三條不同的線上。它會使一個不錯的盒子,如果你關心那些事情.. – Thomas

5

^=運算符的計算順序是明確界定。沒有明確定義的是ab的修改順序。

a ^= b ^= a ^= b; 

相當於

a ^= (b ^= (a ^= b)); 

它的參數進行評估之前,操作員無法評估,所以它肯定會執行a ^= b第一。

這是未定義行爲的原因在於,爲了讓編譯器在優化時有更大的靈活性,允許以任意順序修改變量值。它可以選擇這樣做:

int a1 = a^b; 
int b1 = b^a1; 
int a2 = a^b1; 
a = a1; 
a = a2; 
b = b1; 

或本:

int a1 = a^b; 
int b1 = b^a1; 
a = a1; 
int a2 = a^b1; 
a = a2; 
b = b1; 

,甚至這樣的:

int a1 = a^b; 
int b1 = b^a1; 
int a2 = a^b1; 
a = a2; 
a = a1; 
b = b1; 

如果編譯器只能選擇這三個方法可以做到的事情之一,這隻會是「未說明」的行爲。然而,這個標準更進一步,並且使其成爲「未定義」行爲,這基本上允許編譯器假設它甚至不會發生。

+0

首先解釋是最終的,但我無法理解的原因:*'預修改或修改後的值* –

+1

這是誤導。在評估完成後,沒有什麼能夠防止表達的副作用發生。 'a^= b'的結果是'a^b',作爲副作用,'a'被設置爲該結果。 *當*'a'設置爲該結果未指定時。特別是,在外部'a^= ...'開始之前,沒有任何事情要求它完成*。 – hvd

+0

@hvd:我同意。我試圖對其進行修改以使其更清晰。 –

相關問題