前言
在c/c++ 的項目編譯時經常會遇到 “comp.c:59:42: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]” 這種錯誤。作為一個”合格的程序員“ 對這種編譯告警,通常的處理是忽略,畢竟大家一致的觀點是:只有“warning”不算問題!
下面給出一個小case:
#include <stdio.h> int sum_elements(int a[], unsigned int length) { int i; int result = 0; for (i = 0; i <= length-1; i++) result += a[i]; return result; } int main(int argc, char *argv[]) { int a[] = {1, 2, 3}; int m = sum_elements(a, 0); printf("%d\n", m); return 0; }
一路輕車熟路,編譯運行,但是得到了一個段錯誤。。。
root@HF-LEE:/mnt/c/Users/Q/Desktop# g++ -Wall -g aaa.c -o aaa aaa.c: In function ‘int sum_elements(int*, unsigned int)’: aaa.c:7:43: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] for (i = 0; i <= length-1; i++) ~~^~~~~~~~~~~ root@HF-LEE:/mnt/c/Users/Q/Desktop# ./aaa Segmentation fault (core dumped) root@HF-LEE:/mnt/c/Users/Q/Desktop#
這里發生了什么呢,看代碼只有一個數組,那必然是越界了!!!(問題分析參考:https://www.cnblogs.com/idorax/p/6881996.html)老鳥都會在這里加上強制類型轉換來解決問題。但是問題解決了,下次呢,下次還是會有這樣的問題存在!所以我們需要了解下問題的本質,以及如何避免。
沿用知乎里常用的一句話:“先問是不是,在問為什么?”
無符號數帶來的問題:
如前面示例所示,無符號數與有符號數比較時帶來的問題是最容易忽略的,也有可能是最致命的。如果循環中出現無符號數可能會引起死循環,數組中出現無符號可能會導致數組越界。這當然不能把問題歸咎於無符號數,更應該說是有符號數帶來的思維定勢引起的問題。
無符號數帶來的問題通常是在數值比較時,與無符號數溢出時才會體現。分別是默認的類型轉換與邊界條件檢測不正確導致的。
既然無符號會有問題,那么我們是不是可以拋棄呢??? 你可以采用java那套做法,在習慣上從不使用無符號數(java有這種方案),但是你會在默寫特定場景遇到問題:如網絡編程,串口讀寫。。。
如何合理的使用無符號數:
這是問題的核心了!下面是我總結的一些內容
1. 在位運算、模運算、回繞溢出利用較多的算法實現中(比如各種加密學算法、編碼、壓縮算法等)
有符號數的符號位在進行位運算時候會造成一些迷惑,位運算中如果采用無符號數會大大減少處理問題時對語言上的思考,可以更專心關注實際問題。
2. 在網絡收發,串口讀寫時候使用無符號數
TCP/IP 經常遇到無符號數,比如IP的表示,我們可以用 ip2long 把點分十進制 ip 轉成一個 unsigned int 來表示,這會帶來很大方便。串口讀寫的流更多的是用 unsigned char ,最常見的一個問題是 unsigned char 可以避免日志輸出時候按照有符號輸出造成的 ‘0xff’ 迷惑人的前綴。
3. 避免有符號數與無符號數的直接接觸,包括比較,運算
無符號數與有符號數比較時,編譯器會發出警告。同時編譯器內部也存在一套默認的類型轉換規則(編譯器自動進行,用戶無感知)。大致分為3類(如有錯誤請指正)(說明:在計算機里,負數使用反碼表示的)
先頂一下規則:有符號(int),無符號(unsigned int),非無符號(除 int 與 unsigned int外的類型,如char,unsigned char),非有符號(與前面同理)。
有符號與無符號比較:有符號數會轉換成無符號數來進行比較(如int 與 unsigned int 比較,int 轉換成 unsigned int)。
有符號與非無符號數比較:非無符號轉化成有符號(如int 與 unsigned char比較,unsigned char 轉換成 int)。
無符號與非有符號數比較:非有符號轉化成有符號(如unsigned int 與 char比較,char 轉換成 unsigned int)。
4. 不要只因為某個數不可能為負就用無符號數
因為這雖然看起來很合要求,但是當無符號溢出時候帶來的問題卻很可能致命。