一個(gè)由無符號類型引發(fā)的微妙的錯(cuò)誤
#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;
}
- 這個(gè)程序若用(1)或(2)的判斷條件會輸出:
sizeof(array) / sizeof(char) = 6
strlen(array) = 5
加上強(qiáng)制類型轉(zhuǎn)換(int)才會輸出:x = 104
(1)原因是sizeof操作符(注意它不是一個(gè)函數(shù)蓝撇,也可以有 sizeof char;
和sizeof(char);
效果一樣)的返回值類型是size_t
牺六,而typedef unsigned int size_t;
所以在執(zhí)行表達(dá)式d <= sizeof(array)/sizeof(char)
時(shí),右邊表達(dá)式結(jié)果為無符號整型6底挫,因此會將左邊的整型d裝換成無符號整型(-1(在計(jì)算機(jī)中以1的補(bǔ)碼形式存在為二進(jìn)制1111...11111)將變成變成一個(gè)超級大的整數(shù))恒傻,因此判斷結(jié)果是false。
改正方法:
d <= (int)sizeof(array)/sizeof(char)
,人為增加強(qiáng)制類型轉(zhuǎn)換建邓,這樣就不必由編譯器來選擇結(jié)果的類型了盈厘。
(2)同理,strlen()函數(shù)的返回值類型也是size_t
官边,而typedef unsigned int size_t;
沸手。但是注意strlen()函數(shù)和sizeof操作符的區(qū)別,sizeof會多計(jì)算一個(gè)結(jié)束符'\0'的大小注簿,因此會多1契吉,而strlen()函數(shù)總是只計(jì)算實(shí)際字符串字符個(gè)數(shù)。
改正方法:
d <= (int)strlen(array)
,人為增加強(qiáng)制類型轉(zhuǎn)換诡渴,這樣就不必由編譯器來選擇結(jié)果的類型了捐晶。
對無符號類型的建議(《C專家編程》第24頁)
1、盡量不要在代碼中使用無符號類型,以免增加不必要的麻煩惑灵,尤其是山上,不要僅僅因?yàn)闊o符號類型不存在負(fù)值(如年齡)而用它來表示數(shù)量,可以多增加if-else判斷數(shù)量的合法性也比用無符號類型更安全英支。否則一個(gè)int的 -1佩憾,由編譯器自動(dòng)升級成無符號型int,將會變成一個(gè)非常大的數(shù)潭辈。
2鸯屿、如果用了,應(yīng)該在表達(dá)式中使用強(qiáng)制類型轉(zhuǎn)換把敢,使操作數(shù)均為有符號或者無符號類型寄摆,這樣就不必由編譯器來選擇結(jié)果的類型,可以避免微妙的錯(cuò)誤發(fā)生修赞。
3婶恼、只有在使用位段和二進(jìn)制掩碼時(shí),最好去用無符號數(shù)柏副。這里就要談到正數(shù)和負(fù)數(shù)在計(jì)算機(jī)中的二進(jìn)制存儲方式了勾邦。正數(shù)簡單,就是原碼二進(jìn)制形式存儲割择,如int型32bit位int a = 1;
那么a在計(jì)算機(jī)中存儲為二進(jìn)制000...(總共31個(gè)0)...0001眷篇。但是負(fù)數(shù)就比較復(fù)雜,要用原碼求反碼再加1得到的補(bǔ)碼的二進(jìn)制形式存儲在計(jì)算機(jī)中荔泳,如int b = -1;
原碼和a一樣蕉饼,反碼就是111...(總共31個(gè)1)...1110,然后反碼加1得到補(bǔ)碼111...(總共32個(gè)1)...1111玛歌∶粮郏可見b并不是簡單的存儲為二進(jìn)制100...(總共30個(gè)0)...0001。因此在使用位段和二進(jìn)制掩碼這些需要按bit位操作的情況下支子,為了在使用時(shí)讓我們腦子里想的和計(jì)算機(jī)處理的一致创肥,盡量都用無符號類型,就可以避免負(fù)數(shù)在計(jì)算機(jī)中的特殊存儲方式帶來的影響值朋。
《注意:負(fù)數(shù)要以10進(jìn)制輸出時(shí)叹侄,又要將計(jì)算機(jī)中存儲的補(bǔ)碼,先減1求得反碼吞歼,再按位求反得到原碼圈膏,然后將原碼轉(zhuǎn)換成10進(jìn)制輸出并在前面添加一個(gè)‘-’號》
使用無符號類型的優(yōu)缺點(diǎn)
- 比如int類型和unsigned int類型,同樣是占用4字節(jié)(32bit位)篙骡,int類型的取值范圍是(-2^31到2^31)稽坤,而unsigned int類型的取值范圍是(0到2^32)丈甸,可見在利用正數(shù)的這一半時(shí)無符號數(shù)的可用范圍是有符號的兩倍。有符號數(shù)最高bit位的0或1只能表示+-符號尿褪,不能表示數(shù)值睦擂,浪費(fèi)了。