再有面試官問你如何不用第三個變量實作交換兩個變量的值,你可以直接回答說他狗屁不通。
交換兩個變量的值,最常見的寫法是
int i , j ;
int temp ;
temp = i ;
i = j ;
j = temp ;
這種寫法相信任何學過程式設計語言的都知道。
然而有些着三不着兩的極品面試官卻喜歡追問,不用中間變量應該怎麼寫?這一追問不要緊,追出了很多邪門歪道,例如
int i , j ;
i = i + j;
j = i - j ;
i = i - j ;
這樣的寫法,非常晦澀,但擋不住這樣寫的人為少定義了一個變量而洋洋得意。然而這樣寫真的行得通嗎?運作一下下面的代碼就明白了。
#include <stdio.h>
#include <limits.h>
int main( void )
{
int i , j ;
int temp ;
i = INT_MAX ;
j = INT_MAX - 1;
puts("交換前:");
printf("i = %d , j = %d \n" , i , j );
temp = i ;
i = j ;
j = temp ;
puts("交換後:");
printf("i = %d , j = %d \n" , i , j );
i = INT_MAX ;
j = INT_MAX - 1;
puts("交換前:");
printf("i = %d , j = %d \n" , i , j );
i = i + j ;
j = i - j ;
i = i - j ;
puts("交換後:");
printf("i = %d , j = %d \n" , i , j );
return 0;
}
運作結果通常都是荒誕不經的。原因很簡單, 計算 i + j 時發生溢出,而溢出在C語言中是一種未定義行為,是以得到什麼樣的荒誕結果都不奇怪。
更極品的回答是用乘除法。
i = i * j;
j = i / j;
i = i / j;
這不明擺着更容易溢出嗎?甚至會發生除以0的錯誤。可居然還有人認為“其實不然”,“不會溢出”。嚴重懷疑這是三鹿奶粉喝多了的後遺症。
有的面試官可能自以為自己還沒愚蠢到這種程度,因為他心目中的“标準答案”是通過異或運算。
i ^= j;
j ^= i;
i ^= j;
這種寫法雖然不會産生溢出,但使用條件受限,因為隻能用于整數類型,浮點類型、結構體類型等完全不适用。而且除了少用了一個臨時變量,沒有其他任何好處。
有些人天真地以為這種寫法對于整數類型變量來說會更快,其實這種猜測毫無根據。很難說異或運算就比指派快,據一份公開發表的資料,在某環境下,異或用時為0.050μs,而指派運算隻用0.036μs。
就可讀性而言,異或寫法顯然如同蝌蚪文天書。
更有極品将異或寫法發揮到了極限:
i ^= j ^= i ^= j;
對于C語言來說,這種寫法大錯特錯。因為C語言規定在兩個序點之間同一個資料對象最多隻能改變一次,否則就是未定義行為。(參見 “牙裡長嘴”和“a+=a-=a*a” )這種代碼壓根就不可以寫。如果運作結果碰巧正确,那麼隻是瞎貓碰上死耗子而已。況且已有報告稱在某些環境下這種寫法的運作結果是錯誤的。
異或寫法還有一個潛在的問題,比如
#include <stdio.h>
void swap( int * , int * );
int main( void )
{
int i = 5 , j = 10 ;
puts("交換前:");
printf("i = %d , j = %d \n" , i , j );
swap( & i , & j);
puts("交換後:");
printf("i = %d , j = %d \n" , i , j );
//自己和自己交換
puts("交換前:");
printf("i = %d\n" , i );
swap( & i , & i);
puts("交換後:");
printf("i = %d\n" , i );
return 0;
}
void swap( int * p , int * q )
{
* p ^= * q ;
* q ^= * p ;
* p ^= * q ;
}
這段代碼運作的結果是:
交換前:
i = 5 , j = 10
交換後:
i = 10 , j = 5
i = 10
i = 0
不難發現,如果swap()函數的兩個實參為指向同一資料對象的指針時,結果是錯誤的。
當然,你可能認為自己和自己交換有些無聊。但我們不能排除有些算法可能存在這種交換的可能性。
除了這種可能性,還有另外一種可能性,那就是你把代碼寫錯了,多寫了一次不必要的變量自己和自己的交換。如果你用的是使用中間變量的算法,那麼除了代碼有瑕疵,運作結果的正确性還是有保證的。但如果你用的是異或的辦法,軟體災難則是召之即來——保準比打車來的還快。
是以結論就是,不要用異或的辦法交換變量值。
寫到這裡,本應打住。不過偶然興起,心血來潮,随手一搜,竟發現還有更多的奇葩寫法。
某java-er竟然一本正經地提出下面兩種方法:
a = a + b - (b = a);
b = a + (a = b)*0;
我不清楚在java裡這是否成立,但在C語言中,這都屬于未定義行為。令人拍案的是,該java-er竟然洋洋得意地寫到:
"這是java語言寫的,但是語言不分種類,任何方法都是通用的,推薦使用a=b+(b=a)*0和a^b的方法,有些環境也許會不通過,但是方法和思想不會錯得"
SHIT!