C++的一道筆試題

void swap_int(int *a,int *b){
  *a=*a+*b;
  *b=*a-*b;
  *a=*a-*b;
}

以下說法正確的是:
  • 結果不正確,因爲會溢出,用位與的方式就沒問題
  • 結果正確,即使會溢出
  • 結果正確,不會溢出

  • 其他選項都不對
設整形變量*a、*b的位表示爲
*a = n31n30 ··· n0
*b = m31m30 ··· m0
只有當*a > 0 && *b > 0 或 *a < 0 && *b < 0時纔會發生溢出。兩者類似,只證明均大於0時的情況。
必須擴展額外一位才能夠容納正確的結果,'|'左邊爲擴展位。
*a = 0|0n30 ··· n= n30*230 +  n29*229 + ··· + n0*20 = N
*b = 0|0m30 ··· m0 = m30*230 +  m29*229 + ··· + m0*20 = M
若和溢出,則33位表示必爲
*a + *b = 0|1b30 ··· b= -231 + b30*230 +  b29*229 + ··· + b0*2=  2 31  + B  
計算機將得到的33位結果truncate回原來的32位,即丟棄第33位(0)變爲:
*a + *b =    1b30 ··· b= -231 + b30*230 +  b29*229 + ··· + b0*20 = -2  31   + B ②
正確的真實值是①,溢出結果爲②,可見溢出結果=真實值-2 32 
則*b = *a - *b = ② - *b =  ① - 232 - *b = *a + *b - 232 - *b = -232 + *a
最後一步,來看 -232 + *a  == *a 成立否?
0 < *a < 231, 則 -232 < -232 + *a < -231,和仍需要擴展1位方能表示:
*a    = 0|0n30 ··· n= n30*230 +  n29*229 + ··· + n0*20 = N
-232 = 1|0000 ··· 00
 和的位表示爲
-232 + *a = 1|0n30 ··· n= n30*230 +  n29*229 + ··· + n0*20
同樣,計算機把33位結果truncate回32位(丟棄第33位)得到:
-232 + *a =  0n30 ··· n= n30*230 +  n29*229 + ··· + n0*20 = *a
可見-232 + *a  == *a 是成立的。因此儘管溢出了,但仍能正確交換。

按照我們的慣性思維,加減法會造成溢出,但是先加後減之後原來溢出的又溢回來了,所以運行結果是正確的。
以後筆/面試過程中還要注意運算溢出等問題。還有switch中是否有break,或運算的短路,fork後的代碼是要被
重複執行的等等問題,這些問題平時編程當然都會注意,但是出成題目還是很有迷惑性的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章