您好,歡迎進入深圳市穎特新科技有限公司官方網(wǎng)站!
在數(shù)學(xué)中,數(shù)字有正負(fù)之分。在C語言中也是一樣,short、int、long 都可以帶上正負(fù)號,例如:
//負(fù)數(shù) short a1 = -10; short a2 = -0x2dc9; //十六進制 //正數(shù) int b1 = +10; int b2 = +0174; //八進制 int b3 = 22910; //負(fù)數(shù)和正數(shù)相加 long c = (-9) + (+12);
如果不帶正負(fù)號,默認(rèn)就是正數(shù)。
符號也是數(shù)字的一部分,也要在內(nèi)存中體現(xiàn)出來。符號只有正負(fù)兩種情況,用1位(Bit)就足以表示;C語言規(guī)定,把內(nèi)存的最高位作為符號位。以 int 為例,它占用 32 位的內(nèi)存,0~30 位表示數(shù)值,31 位表示正負(fù)號。如下圖所示:
在編程語言中,計數(shù)往往是從0開始,例如字符串 "abc123",我們稱第 0 個字符是 a,第 1 個字符是 b,第 5 個字符是 3。這和我們平時從 1 開始計數(shù)的習(xí)慣不一樣,大家要慢慢適應(yīng),培養(yǎng)編程思維。
C語言規(guī)定,在符號位中,用 0 表示正數(shù),用 1 表示負(fù)數(shù)。例如 int 類型的 -10 和 +16 在內(nèi)存中的表示如下:
short、int 和 long 類型默認(rèn)都是帶符號位的,符號位以外的內(nèi)存才是數(shù)值位。如果只考慮正數(shù),那么各種類型能表示的數(shù)值范圍(取值范圍)就比原來小了一半。
但是在很多情況下,我們非常確定某個數(shù)字只能是正數(shù),比如班級學(xué)生的人數(shù)、字符串的長度、內(nèi)存地址等,這個時候符號位就是多余的了,就不如刪掉符號位,把所有的位都用來存儲數(shù)值,這樣能表示的數(shù)值范圍更大(大一倍)。
C語言允許我們這樣做,如果不希望設(shè)置符號位,可以在數(shù)據(jù)類型前面加上 unsigned 關(guān)鍵字,例如:
unsigned short a = 12; unsigned int b = 1002; unsigned long c = 9892320;
這樣,short、int、long 中就沒有符號位了,所有的位都用來表示數(shù)值,正數(shù)的取值范圍更大了。這也意味著,使用了 unsigned 后只能表示正數(shù),不能再表示負(fù)數(shù)了。
如果將一個數(shù)字分為符號和數(shù)值兩部分,那么不加 unsigned 的數(shù)字稱為有符號數(shù),能表示正數(shù)和負(fù)數(shù),加了 unsigned 的數(shù)字稱為無符號數(shù),只能表示正數(shù)。
請讀者注意一個小細節(jié),如果是unsigned int
類型,那么可以省略 int ,只寫 unsigned,例如:
unsigned n = 100;
它等價于:
unsigned int n = 100;
無符號數(shù)可以以八進制、十進制和十六進制的形式輸出,它們對應(yīng)的格式控制符分別為:
unsigned short | unsigned int | unsigned long | |
---|---|---|---|
八進制 | %ho | %o | %lo |
十進制 | %hu | %u | %lu |
十六進制 | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
上節(jié)我們也講到了不同進制形式的輸出,但是上節(jié)我們還沒有講到正負(fù)數(shù),所以也沒有關(guān)心這一點,只是“籠統(tǒng)”地介紹了一遍,F(xiàn)在本節(jié)已經(jīng)講到了正負(fù)數(shù),那我們就再深入地說一下。
嚴(yán)格來說,格式控制符和整數(shù)的符號是緊密相關(guān)的,具體就是:
那么,如何以八進制和十六進制形式輸出有符號數(shù)呢?很遺憾,printf 并不支持,也沒有對應(yīng)的格式控制符。在實際開發(fā)中,也基本沒有“輸出負(fù)的八進制數(shù)或者十六進制數(shù)”這樣的需求,我想可能正是因為這一點,printf 才沒有提供對應(yīng)的格式控制符。
下表全面地總結(jié)了不同類型的整數(shù),以不同進制的形式輸出時對應(yīng)的格式控制符(--
表示沒有對應(yīng)的格式控制符)。
short | int | long | unsigned short | unsigned int | unsigned long | |
---|---|---|---|---|---|---|
八進制 | -- | -- | -- | %ho | %o | %lo |
十進制 | %hd | %d | %ld | %hu | %u | %lu |
十六進制 | -- | -- | -- | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
有讀者可能會問,上節(jié)我們也使用 %o 和 %x 來輸出有符號數(shù)了,為什么沒有發(fā)生錯誤呢?這是因為:
對于一個有符號的正數(shù),它的符號位是 0,當(dāng)按照無符號數(shù)的形式讀取時,符號位就變成了數(shù)值位,但是該位恰好是 0 而不是 1,所以對數(shù)值不會產(chǎn)生影響,這就好比在一個數(shù)字前面加 0,有多少個 0 都不會影響數(shù)字的值。
如果對一個有符號的負(fù)數(shù)使用 %o 或者 %x 輸出,那么結(jié)果就會大相徑庭,讀者可以親試。
可以說,“有符號正數(shù)的最高位是 0”這個巧合才使得 %o 和 %x 輸出有符號數(shù)時不會出錯。
再次強調(diào),不管是以 %o、%u、%x 輸出有符號數(shù),還是以 %d 輸出無符號數(shù),編譯器都不會報錯,只是對內(nèi)存的解釋不同了。%o、%d、%u、%x 這些格式控制符不會關(guān)心數(shù)字在定義時到底是有符號的還是無符號的:
說得再直接一些,我管你在定義時是有符號數(shù)還是無符號數(shù)呢,我只關(guān)心內(nèi)存,有符號數(shù)也可以按照無符號數(shù)輸出,無符號數(shù)也可以按照有符號數(shù)輸出,至于輸出結(jié)果對不對,那我就不管了,你自己承擔(dān)風(fēng)險。
下面的代碼進行了全面的演示:
運行結(jié)果:
a=0100, b=0xffffffff, c=720
m=-1, n=-2147483648, p=100
對于絕大多數(shù)初學(xué)者來說,b、m、n 的輸出結(jié)果看起來非常奇怪,甚至不能理解。按照一般的推理,b、m、n 這三個整數(shù)在內(nèi)存中的存儲形式分別是:
當(dāng)以 %x 輸出 b 時,結(jié)果應(yīng)該是 0x80000001;當(dāng)以 %hd、%d 輸出 m、n 時,結(jié)果應(yīng)該分別是 -7fff、-0。但是實際的輸出結(jié)果和我們推理的結(jié)果卻大相徑庭,這是為什么呢?
注意,-7fff 是十六進制形式。%d 本來應(yīng)該輸出十進制,這里只是為了看起來方便,才改為十六進制。
其實這跟整數(shù)在內(nèi)存中的存儲形式以及讀取方式有關(guān)。b 是一個有符號的負(fù)數(shù),它在內(nèi)存中并不是像上圖演示的那樣存儲,而是要經(jīng)過一定的轉(zhuǎn)換才能寫入內(nèi)存;m、n 的內(nèi)存雖然沒有錯誤,但是當(dāng)以 %d 輸出時,并不是原樣輸出,而是有一個逆向的轉(zhuǎn)換過程(和存儲時的轉(zhuǎn)換過程恰好相反)。
也就是說,整數(shù)在寫入內(nèi)存之前可能會發(fā)生轉(zhuǎn)換,在讀取時也可能會發(fā)生轉(zhuǎn)換,而我們沒有考慮這種轉(zhuǎn)換,所以才會導(dǎo)致推理錯誤。
上一篇:N76E003之SPI
下一篇:N76E003之IAP
掃碼關(guān)注我們
傳真:0755-82591176
郵箱:vicky@yingtexin.net
地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤豐創(chuàng)業(yè)園A棟2樓A08