面试奇葩——交换两变量值的一些邪门歪道
交換兩個變量的值,最常見的寫法是
int i , j ;int temp ;temp = i ;i = j ;j = temp ;這種寫法相信任何學(xué)過程序設(shè)計(jì)語言的都知道。
然而有些著三不著兩的極品面試官卻喜歡追問,不用中間變量應(yīng)該怎么寫?這一追問不要緊,追出了很多邪門歪道,例如
int i , j ;i = i + j;j = i - j ;i = i - j ;這樣的寫法,非常晦澀,但擋不住這樣寫的人為少定義了一個變量而洋洋得意。然而這樣寫真的行得通嗎?運(yùn)行一下下面的代碼就明白了。?
#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; }運(yùn)行結(jié)果通常都是荒誕不經(jīng)的。原因很簡單, 計(jì)算 i + j 時發(fā)生溢出,而溢出在C語言中是一種未定義行為,所以得到什么樣的荒誕結(jié)果都不奇怪。
更極品的回答是用乘除法。?
i = i * j;j = i / j;i = i / j;這不明擺著更容易溢出嗎?甚至?xí)l(fā)生除以0的錯誤。可居然還有人認(rèn)為“其實(shí)不然”,“不會溢出”。嚴(yán)重懷疑這是三鹿奶粉喝多了的后遺癥。
有的面試官可能自以為自己還沒愚蠢到這種程度,因?yàn)樗哪恐械摹皹?biāo)準(zhǔn)答案”是通過異或運(yùn)算。
i ^= j;j ^= i;i ^= j;這種寫法雖然不會產(chǎn)生溢出,但使用條件受限,因?yàn)橹荒苡糜谡麛?shù)類型,浮點(diǎn)類型、結(jié)構(gòu)體類型等完全不適用。而且除了少用了一個臨時變量,沒有其他任何好處。
有些人天真地以為這種寫法對于整數(shù)類型變量來說會更快,其實(shí)這種猜測毫無根據(jù)。很難說異或運(yùn)算就比賦值快,據(jù)一份公開發(fā)表的數(shù)據(jù),在某環(huán)境下,異或用時為0.050μs,而賦值運(yùn)算只用0.036μs。
就可讀性而言,異或?qū)懛@然如同蝌蚪文天書。
更有極品將異或?qū)懛òl(fā)揮到了極限:
i ^= j ^= i ^= j;對于C語言來說,這種寫法大錯特錯。因?yàn)镃語言規(guī)定在兩個序點(diǎn)之間同一個數(shù)據(jù)對象最多只能改變一次,否則就是未定義行為。(參見 “牙里長嘴”和“a+=a-=a*a”?)這種代碼壓根就不可以寫。如果運(yùn)行結(jié)果碰巧正確,那么只是瞎貓碰上死耗子而已。況且已有報告稱在某些環(huán)境下這種寫法的運(yùn)行結(jié)果是錯誤的。
異或?qū)懛ㄟ€有一個潛在的問題,比如
#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 ; }這段代碼運(yùn)行的結(jié)果是:
交換前:
i = 5 , j = 10
交換后:
i = 10 , j = 5
交換前:
i = 10
交換后:
i = 0
不難發(fā)現(xiàn),如果swap()函數(shù)的兩個實(shí)參為指向同一數(shù)據(jù)對象的指針時,結(jié)果是錯誤的。
當(dāng)然,你可能認(rèn)為自己和自己交換有些無聊。但我們不能排除有些算法可能存在這種交換的可能性。
除了這種可能性,還有另外一種可能性,那就是你把代碼寫錯了,多寫了一次不必要的變量自己和自己的交換。如果你用的是使用中間變量的算法,那么除了代碼有瑕疵,運(yùn)行結(jié)果的正確性還是有保證的。但如果你用的是異或的辦法,軟件災(zāi)難則是召之即來——保準(zhǔn)比打車來的還快。
所以結(jié)論就是,不要用異或的辦法交換變量值。
寫到這里,本應(yīng)打住。不過偶然興起,心血來潮,隨手一搜,竟發(fā)現(xiàn)還有更多的奇葩寫法。
某java-er竟然一本正經(jīng)地提出下面兩種方法:?
a = a + b - (b = a); b = a + (a = b)*0;我不清楚在java里這是否成立,但在C語言中,這都屬于未定義行為。令人拍案的是,該java-er竟然洋洋得意地寫到:
"這是java語言寫的,但是語言不分種類,任何方法都是通用的,推薦使用a=b+(b=a)*0和a^b的方法,有些環(huán)境也許會不通過,但是方法和思想不會錯得"
SHIT!
總結(jié)
以上是生活随笔為你收集整理的面试奇葩——交换两变量值的一些邪门歪道的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: qemu核心机制分析-协程corouti
- 下一篇: 每天读5分钟,受益匪浅、