-1>1?! unsigned int的世界不簡單

編程語言提供了很多的基本數據類型,比如char,int,float,double等等。在C和C++的世界中,還有一種類型,叫做無符號數據,修飾符位unsigned,比如今天要說的unsigned int。引入特殊的類型,一方面帶來了好處,一方面也留下了隱患。

一、有符號數與無符號數誰大誰小

上代碼:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a = -1;
    unsigned int b = 1;

    if(a > b)
        printf("a > b, a = %d, b = %u\n", a, b);
    else
        printf("a <= b, a = %d, b = %u\n", a, b);

    return 0;
}

結果爲:

什麼?-1竟然大於1?從結果上看,的確是這樣的。爲什麼從這樣呢?這樣從C++對同時包含有符號數與無符號數的表達式的處理說起。

二、C++底層怎麼處理的

當執行一個運算時(如這裏的a>b),如果它的一個運算數是有符號的而另一個數是無符號的,那麼C語言會隱式地將有符號參數強制轉換類型爲無符號數,並假設這兩個數都是非負的,來執行這個運算。這種方法對於標準的算術運算來說並無多大差異,但是對於像小於“<”和大於“>”這樣的運算就可能產生非直觀的結果。

對應上面的例子,就是先把-1這個有符號數強制轉換成無符號數,再與1比較,並假設兩個數都是非負的。那麼-1轉換成無符號數是多少呢?在32位或者64位機器上,-1對應的無符號數是4 294 967 295,即32位的無符號數的最大值(UMax),所以if中的條件總是爲真。

要想這段代碼正常執行,我們需要怎麼辦呢?很簡單,把if語句改爲if(a > (int)b)即可。這樣程序就會認爲是兩個有符號數在進行比較,-1就不會隱式地轉換爲無符號數而變成UMax。
可能你已經有一個問題,爲什麼使用強制類型,把變量b的類型變成int程序就能正常,而-1轉換成無符號數爲什麼會是4 294 967 295呢?這就得從整型數據在計算機中的表示和C語言對待強制類型轉換的方式說起。
我們知道,整數在計算機中通常是以補碼的形式存在的,而-1的補碼(用4個字節儲存)爲1111,1111,1111,1111。而C語言對於強制類型轉換是怎麼處理的呢?對大多數C語言的實現,處理同樣字長的有符號數和無符號數之間的相互轉換的一般規則是:數值可能會改變,但是位模式不變。也就是說,將unsigned int強制類型轉換成int,或將int轉換成unsigned int底層的位表示保持不變。
也就是說,即使是-1轉換成unsigned int之後,它在內存中的表示還是沒有改變,即1111,1111,1111,1111。我們知道在計算機的底層,數據是沒有類型可言的,所有的數據非0即1。數據類型只有在高層的應用程序纔有意義,也就是說,同樣的儲存表示對於應用程序而言可能對應着不同的數據,例如1111,1111,1111,1111對於有符號數而言它表示-1,但對於無符號數而言,它表示UMax,但是它們的底層存儲都是一樣的。現在你應該明白爲什麼-1轉換成無符號數之後,就成了UMax了吧。

三、查看數據的底層表示

上代碼,裏面有個show_byte函數,可以把從指針start開始的len個字節用16進制數的形式打印。

#include <stdio.h>
#include <stdlib.h>

void show_bytes(unsigned char *start, int len)
{
    int i = 0;
    for(; i < len; ++i)
        printf(" %.2x", start[i]);
    printf("\n");
}

int main()
{
    int a = -1;
    unsigned int b = 4294967295;

    printf("a = %d, a = %u\n", a, a);
    printf("b = %d, b = %u\n", b, b);

    show_bytes((unsigned char*)&a, sizeof(int));
    show_bytes((unsigned char*)&b, sizeof(unsigned int));

    return 0;  
}

結果爲:

printf函數中,%u表示以無符號數十進制的形式輸出,%d表示以有符號十進制的形式輸出。通過show_bytes函數,我們可以看到,-1與4 294 967 295的底層表示是一樣的,它們的位全部都是全1,即每個字節表示爲ff。

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