計算機系統基礎摘記——c語言數值常量的類型

1 c編譯器怎麼確定數值常量的類型

首先明確一下這裏所說的數值常量指的是程序中的一個數字,比如:

if (1 < 2)
{
}

其中12就是數值常量。

就以數字1爲例,1在很多類型的表示範圍之內,比如shortintunsigned int等,那麼編譯器會將1解讀成哪個類型呢?這是由c標準規定的,對於不同版本的c標準,相關規定的具體內容有所差異,對於c90,相關規定如下表所示:

範圍 類型
0~231-1 int
231~232-1 unsigned int
232~263-1 long long
263~264-1 unsigned long long

而對於c99,相關標準有了些變化:

範圍 類型
0~231-1 int
231~263-1 long long
263~264-1 unsigned long long

細心的同學可能會發現,上表中沒有給出負值的情況,比如-10。編譯器在遇到負值的時候,先不看符號,而是根據數字來確定類型,然後再處理負號。仍舊拿-10打比方,編譯器根據10來確定類型爲int,然後再處理負號。

2 c語言數值常量的陷阱

爲什麼要強調數值常量的類型呢?因爲這裏面有一些陷阱需要注意。以c90標準來說,考慮如下的表達式:

-2147483648 < 2147483647

很多人第一反應都是這個表達式爲true,畢竟從數值上看這是顯然的。但是,支持c90標準的編譯器可不這麼認爲!關鍵是-2147483648,編譯器根據2147483648也就是231,確定這個數值的類型爲unsigned int。再考慮負號,根據負數的補碼的轉換規則:對應正數(絕對值)按位取反再加1

8000_0000 按位取反⇒ 7fff_ffff 再加1⇒ 8000_0000

就這樣,編譯器把-2147483648硬是給處理成了unsigned int類型的2147483648,而2147483648是大於2147483647的。但是編譯器又不能把2147483648解讀成int類型,因爲補碼錶示是不對稱的,int類型最小可以表示到-2147483648,但最大隻能表示到2147483647。如果能把2147483648表示成更大的有符號類型比如long long就不會有上述問題了,c99標準正是這麼做的。不過爲了寫出不同版本的標準下,行爲一致的程序,我們還是要注意這個細節。

爲了加深理解,不妨再來看幾個例子(仍然根據c90標準):
例1

int i = -2147483648;
if (i < 2147483647)
{
	printf("-2147483648 < 2147483647\n")
}

此時,會打印出-2147483648 < 2147483647。上文已說過,編譯器會把-2147483648解讀成一串二進制0x8000_0000 ,因此變量i所在內存存儲的也是這一串二進制,但不同的是,因爲i是int類型,因此會以int來看待這串二進制,一個負數自然是小於2147483647的。

例2

-2147483647 - 1 < 2147483647

對於-2147483647,編譯器根據2147483647確定其爲int類型,然後處理負號:

7fff_ffff 按位取反⇒ 8000_0000 再加1⇒ 8000_0001

-2147483647 - 1則得到8000_0001 - 1 = 8000_0000,由於是按int類型來看,因此8000_0000是一個負數,且是int所能表達的最小負數-2147483648。所以例2的表達式爲true。值得一提的是,INT_MIN通常就定義爲:

#define INT_MIN (-2147483647 - 1)

這樣一來,這個宏不管在c90還是c99標準都能工作的很好。

例3

if (-2147483648 - 1 == 2147483647)
{
	printf("-2147483648 - 1 == 2147483647\n")
}

編譯器會將-2147483648解讀成unsigned int類型的二進制串8000_0000,而8000_0000 - 1 = 7fff_ffff,因此例3中條件成立,會執行printf語句。

3 有符號數和無符號數在一起運算

除了讓編譯器自己去解讀數值常量的類型,我們還可以通過在數值常量後面加上後綴來主動告訴編譯器數值常量的類型。比如0U0u就是告訴編譯器,這是一個unsigned int0。此外,也可以用強制類型轉換來實現這一目標,比如(unsigned)0

值得關注的是,當無符號數和有符號數一起運算時,c語言會進行類型提升,把有符號數按照無符號數看待。不同類型的數混在一起運算時,有一些值得注意的地方,如下表:

關係表達式 類型 結果 說明
0 == 0U 無符號 true 0000_0000 == 0000_0000
-1 < 0 有符號 true -1 < 0
-1 < 0U 無符號 false ffff_ffff > 0000_0000
2147483647 > -2147483647 - 1 有符號 true 231-1 > -231
2147483647U > -2147483647 - 1 無符號 false 231-1 < 231
2147483647 > (int)2147483648U 有符號 true 231-1 > -231
-1 > -2 有符號 true -1 > -2
(unsigned)-1 > -2 無符號 true 231-1 > 231-2

參考文獻

[1] 南大袁春風教授的計算機系統基礎課程

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