1. 程式人生 > >C語言有符號與符號引數的比較

C語言有符號與符號引數的比較

 1.整型無符號引數與有符號引數比較

程式碼段:
#include<cstdio>
int main()
{
	int a=-1;
	unsigned int b=0;
	if(b>a)printf("b>a\n");
	else printf("b<a\n");
	printf("a int unsigned:%u\na in int:%d",a,a);
	getchar();
}

執行結果:
    b<a
    a int unsigned: 4294967295
    a in int: -1

原因:計算機中整數以補碼的形式儲存的【正數的補碼等於原碼;負數的補碼等於反碼加1,而反碼等於原碼符號位不變,其餘各位取反

】,所以取出來的數為兩個補碼值比較。(當執行一個運算時(如這裡的a>b),如果它的一個運算數是有符號的而另一個數是無符號的,那麼C語言會隱式地將有符號 引數強制型別為無符號數,並假設這兩個數都是非負的,來執行這個運算,這種說法可能不合理,這裡實際就是兩個補碼值進行比較)。

-1  = 1000 0000 0000 0000 0000 0000 0000 0001  原碼

-1 =  1111 1111 1111 1111 1111 1111 1111 1110  反碼

-1 =  1111 1111 1111 1111 1111 1111 1111 1111  補碼

解決方案:if(b>(int)a)

正數無符號轉換成有符號型別沒有問題,只是將最高位當做符號位(正數0,負數1)

2.整型無符號與有符號減法

錯誤程式碼:
float sum_elements(float a[], unsigned length)  
{  
    int i = 0;  
    float sum = 0;  
    for(i = 0; i <= length -1; ++i)  
    { 
        sum += a[i];  
    }
    return sum;  
} 

如果我告訴你這是一段有錯的程式碼,可能你也不太相信,因為這個函式的一切看起來是這麼的自然,因為資料的長度(或個數)肯定是一個非負數,所以把length宣告為一個unsigned很合理,計算的資料個數和返回型別也正確。的確如此,但是這都是在length不為0的情況,試想,當呼叫函式時,把0作為引數傳遞給length會發生什麼事情?回想一下前面我們所說的知識,因為length是unsigned型別,所以所有的運算都被隱式地被強制轉換為unsigned型別

,所以length-1(即0-1 = -1),-1對應的無符號型別的值為UMax,所以for迴圈將會迴圈UMax次,陣列也會越界,發生錯誤。

解決方案:for(i = 0; i < length; ++i)  

3. strlen比較

錯誤程式碼:
int strlonger(char *s1, char *s2)  
{  
    return strlen(s1) - strlen(s2) > 0;  
}  

size_t strlen(const char *s); 
注意這裡有一個數據型別size_t,它被定義在stdio.h檔案中,其實它就是unsigned int,一個字串的長度當然不可能為負,這樣的定義顯然是合理的,但是有時卻因為這樣,而存在不少的問題,如函式strlonger的實現。當s1的長度大於等於s2時,這個函式並沒有什麼問題,但是你可以想像,當s1的長度小於s2的長度時,這個函式會返回什麼嗎?沒錯,因為此時strlen(s1) - strlen(s2)為負(從數學的角度來解釋的話),而又由於程式把它作為unsigned為處理,則此時的值肯定是一個比0大的值。換句話來說,這個函式只有在strlen(s1) == strlen(s2)時返回假,其他情況都返回真。

解決方案:

從執行結果來看,確實如此,只要s1與s2長度不等,就返回真。那麼我們在怎麼樣改善這段程式碼呢?其實答案也是很簡單的,所函式改為如下即可:

修改之後:
int strlonger(char *s1, char *s2) 
{ 
    return strlen(s1) > strlen(s2); 
} 

這樣就可以利用兩個無符號數進行直接的比較,而不會因為減法而出現負數(數學上來說)而影響比較結果。