一個由無符號類型引發(fā)的微妙的錯誤
#include <stdio.h>
#include <string.h>
int main()
{
char array[] = "hello";
int d = -1, x;
//(1)//if (d <= sizeof(array) / sizeof(char))
//(2)//if (d <= strlen(array))
//if (d <= (int)sizeof(array) / sizeof(char)) /*輸出:x = 104*/
if (d <= (int)strlen(array)) /*輸出:x = 104*/
{
x = array[d + 1];
printf("x = %d\n", x); //字符'h'對應(yīng)的ASCII碼是104, 'a'是97
}
else
{
printf("sizeof(array) / sizeof(char) = %u\n", sizeof(array) / sizeof(char));
printf("strlen(array) = %u\n", strlen(array));
//用%d輸出也是一樣,都是正數(shù)
}
return 0;
}
- 這個程序若用(1)或(2)的判斷條件會輸出:
sizeof(array) / sizeof(char) = 6
strlen(array) = 5
加上強制類型轉(zhuǎn)換(int)才會輸出:x = 104
(1)原因是sizeof操作符(注意它不是一個函數(shù),也可以有 sizeof char;和sizeof(char);效果一樣)的返回值類型是size_t,而typedef unsigned int size_t;
所以在執(zhí)行表達式d <= sizeof(array)/sizeof(char)時,右邊表達式結(jié)果為無符號整型6,因此會將左邊的整型d裝換成無符號整型(-1(在計算機中以1的補碼形式存在為二進制1111...11111)將變成變成一個超級大的整數(shù)),因此判斷結(jié)果是false。
改正方法:
d <= (int)sizeof(array)/sizeof(char),人為增加強制類型轉(zhuǎn)換,這樣就不必由編譯器來選擇結(jié)果的類型了。
(2)同理,strlen()函數(shù)的返回值類型也是size_t,而typedef unsigned int size_t;。但是注意strlen()函數(shù)和sizeof操作符的區(qū)別,sizeof會多計算一個結(jié)束符'\0'的大小,因此會多1,而strlen()函數(shù)總是只計算實際字符串字符個數(shù)。
改正方法:
d <= (int)strlen(array),人為增加強制類型轉(zhuǎn)換,這樣就不必由編譯器來選擇結(jié)果的類型了。
對無符號類型的建議(《C專家編程》第24頁)
1、盡量不要在代碼中使用無符號類型,以免增加不必要的麻煩,尤其是,不要僅僅因為無符號類型不存在負值(如年齡)而用它來表示數(shù)量,可以多增加if-else判斷數(shù)量的合法性也比用無符號類型更安全。否則一個int的 -1,由編譯器自動升級成無符號型int,將會變成一個非常大的數(shù)。
2、如果用了,應(yīng)該在表達式中使用強制類型轉(zhuǎn)換,使操作數(shù)均為有符號或者無符號類型,這樣就不必由編譯器來選擇結(jié)果的類型,可以避免微妙的錯誤發(fā)生。
3、只有在使用位段和二進制掩碼時,最好去用無符號數(shù)。這里就要談到正數(shù)和負數(shù)在計算機中的二進制存儲方式了。正數(shù)簡單,就是原碼二進制形式存儲,如int型32bit位int a = 1;那么a在計算機中存儲為二進制000...(總共31個0)...0001。但是負數(shù)就比較復(fù)雜,要用原碼求反碼再加1得到的補碼的二進制形式存儲在計算機中,如int b = -1;原碼和a一樣,反碼就是111...(總共31個1)...1110,然后反碼加1得到補碼111...(總共32個1)...1111??梢奲并不是簡單的存儲為二進制100...(總共30個0)...0001。因此在使用位段和二進制掩碼這些需要按bit位操作的情況下,為了在使用時讓我們腦子里想的和計算機處理的一致,盡量都用無符號類型,就可以避免負數(shù)在計算機中的特殊存儲方式帶來的影響。
《注意:負數(shù)要以10進制輸出時,又要將計算機中存儲的補碼,先減1求得反碼,再按位求反得到原碼,然后將原碼轉(zhuǎn)換成10進制輸出并在前面添加一個‘-’號》
使用無符號類型的優(yōu)缺點
- 比如int類型和unsigned int類型,同樣是占用4字節(jié)(32bit位),int類型的取值范圍是(-2^31到2^31),而unsigned int類型的取值范圍是(0到2^32),可見在利用正數(shù)的這一半時無符號數(shù)的可用范圍是有符號的兩倍。有符號數(shù)最高bit位的0或1只能表示+-符號,不能表示數(shù)值,浪費了。