編寫安全代碼——小心有符號數的右移操作

轉載來源:http://blog.chinaunix.net/uid-23629988-id-3018793.html

作者:[email protected]

博客:blog.focus-linux.net     linuxfocus.blog.chinaunix.net

話說有這樣的一段代碼:
  1. #include <stdlib.h>
  2. #include <stdio.h>


  3. static void divide_by_two(int num)
  4. {
  5.     while (num) {
  6.         printf("%d\n", num);
  7.         num /= 2;
  8.     }
  9. }

  10. int main()
  11. {
  12.     int num;
  13.     scanf("%d", &num);

  14.     divide_by_two(num);

  15.     return 0;
  16. }
某天,一個剛畢業的朋友——沒有貶低應屆生的意思哦~~~,開始負責維護這段代碼。他呢,看到num /= 2,想起課程上講過,整數右移一位,就等於除於二,並且右移操作比除法運算要高效的多。於是將 num /=2 改爲了 num = num>>1。

代碼變爲
  1. #include <stdlib.h>
  2. #include <stdio.h>


  3. static void divide_by_two(int num)
  4. {
  5.     while (num) {
  6.         printf("%d\n", num);
  7.         num = num>>1;
  8.     }
  9. }

  10. int main()
  11. {
  12.     int num;
  13.     scanf("%d", &num);

  14.     divide_by_two(num);

  15.     return 0;
  16. }
編譯成功後,當然不能忘了測試:
  1. [xxx@xxx-vm-fc13 test]$ ./a.out
  2. 10
  3. 10
  4. 5
  5. 2
  6. 1
  7. [xxx@xxx-vm-fc13 test]$ ./a.out
  8. 3
  9. 3
  10. 1
這位朋友對於結果很滿意,於是將改動提交到了服務器,下班回家~~~

結果第二天就有同事來找他,說他的程序陷入了死循環:
  1. [xxx@xxx-vm-fc13 test]$ ./a.out
  2. -5
  3. -5
  4. -2
  5. -1
  6. -1
  7. -1
  8. 。。。。。。
  9. -1
這位朋友剛參加工作,第一次提交改動,就造成了這樣的結果,自然很緊張。不是右移一位就等於除以2嗎?究竟是怎麼回事呢?雖然暫時不知道答案,也只能將改動rollback回去。

那麼到底是什麼原因呢?沒錯,右移一位就等於除以2,但是這裏需要加一個條件,這裏指的是正數。而對於有符號整數,且其值爲負數時,在C99標準中對於其右移操作的結果的規定是implementation-defined.

在Linux上的GCC實現中,有符號數的右移操作的實現爲使用符號位作爲補充位。因此-1的右移操作仍然爲0xFFFFFFFF。這導致了死循環。
---------------------------
補充:
其實也說明另外一個問題:測試不充分,針對int類型參數,一定要測試完整樣本數據。

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