[英]Undefined behaviour of operators in XOR swap algorithm?
void swap(int* a, int* b) {
if (a != b)
*a ^= *b ^= *a ^= *b;
}
如上所述*a ^= *b ^= *a ^= *b
只是*a = *a ^ (*b = *b ^ (*a = *a ^ *b))
的快捷方式,可以(例如)第二個*a
被評估(對於XOR)就在第三個*a
被修改之前(由=)?
我是否用C99 / C11 / C ++ 98 / C ++ 11編寫它是否重要?
C ++ 11標准說:
5.17 / 1:賦值運算符(=)和復合賦值運算符從右到左分組 。 (...)在右和左操作數的值計算之后,以及在賦值表達式的值計算之前,對賦值進行排序。
1.9 / 15:如果對標量對象的副作用相對於同一標量對象的另一個副作用或使用相同標量對象的值進行的值計算未被排序,則行為未定義。
所以*a ^= *b
按如下方式排序:
*a
和*b
。 它不是以哪種順序確定的 *a
(*a ^= *b)
現在*b ^= *a ^= *b
,根據優先級規則是*b ^= (*a ^= *b)
:
*b
和(*a ^= *b)
。 它不是以哪種順序確定的。 但由於*b
未被(*a ^= *b)
修改,因此無關緊要。 *b
但現在使用*a ^= *b ^= *a ^= *b
進行未指定的排序 ,這是根據優先級規則*a ^= (*b ^= (*a ^= *b) )
:
*a
和(*b ^= (*a ^= *b) )
。 它不是以哪種順序確定的。 但是*a
由(*b ^= (*a ^= *b) )
修改(*b ^= (*a ^= *b) )
。 因此,結果將取決於首先計算哪個值。 那顯然是UB 假設首先評估*a
,(即在其他任何事情之前):
你會得到它的原始值,它將用(*b ^= (*a ^= *b) )
的值進行xored,即原始*b
xored與原始*a
xored再次與*b
。 這將導致0(將存儲在*a
)。
假設(*b ^= (*a ^= *b) )
第一次評價 ,則其結果是原始*a
,但含量*a
被改變為原始*a
與原來的異或*b
。 因此,這將導致原始*b
(將存儲在*a
)
順便說一句,在兩種情況下, *b
包含原始值*a
xored兩次*b
意味着*b
將包含原始*a
。
結論:這里證明了*b
的最終值由該表達式唯一確定,但*a
的最終值沒有唯一定義(可能有兩個值)。 所以它顯然是一個未知/未定的結果 ! 它可能會交換,也可能會丟失*a
具體取決於您的編譯器。
我在上面已經證明了前兩個復合賦值已經明確指出。 所以我們必須確保在它之后完成最后的復合賦值。 這可以通過逗號運算符來保證:
5.18 / 1:用逗號分隔的一對表達式從左到右計算,左表達式的值被丟棄
因此,以下方法可行:
void safe_swap(int* a, int* b) {
if (a != b)
*b ^= *a ^= *b, *a ^= *b;
}
在某些沒有更多可用內存的嵌入式設備上,人們可能必須在極端條件下使用這種高級技巧。 但它有缺點。
首先,很難理解,如上所述,容易出錯。 然后它可能沒有看起來那么高效。 一些依賴於實現的實驗顯示不太理想的代碼 :3 MOV
和3 XOR
,而使用臨時變量的經典交換只有4 MOV
。 一些非正式的基准測試表明,大部分時間它可能會減慢3%到8%。
順便說一下,經典交換也可以用一個語句寫成:
void modern_swap(int*a, int*b) {
if (a!=b)
tie(*a,*b)=make_pair(*b,*a);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.