閱讀經(jīng)典——《深入理解計(jì)算機(jī)系統(tǒng)》02
- 基本數(shù)據(jù)類型
- 大小端模式
- 整型數(shù)范圍與C標(biāo)準(zhǔn)
- 復(fù)合型類型轉(zhuǎn)換——從short到unsigned
基本數(shù)據(jù)類型
讓我們復(fù)習(xí)一下C語言中基本數(shù)據(jù)類型的字節(jié)數(shù)
名稱 | 32位 | 64位 |
---|---|---|
char | 1 | 1 |
short int | 2 | 2 |
int | 4 | 4 |
long int | 4 | 8 |
long long int | 8 | 8 |
char * | 4 | 8 |
float | 4 | 4 |
double | 8 | 8 |
這其實(shí)是很基礎(chǔ)的知識,但是值得一提的是伺糠,表中只有兩個(gè)數(shù)據(jù)類型在32位和64位計(jì)算機(jī)中使用了不同的字節(jié)數(shù)蒙谓,一個(gè)是long int
,另一個(gè)是char *
训桶。其實(shí)不只是char *
累驮,任何數(shù)據(jù)類型的指針使用的字節(jié)數(shù)都和char *
是一樣的酣倾,所以這里更好的寫法應(yīng)該是type *
。
為了盡量使程序可移植谤专,要小心從32位到64位的變化躁锡。例如,在32位機(jī)器中使用int
保存變量的地址可行置侍,但同樣的程序移植到64位機(jī)器中就成了bug映之。所以不如嘗試用long int
保存變量的地址。這個(gè)時(shí)候墅垮,上面的表格就體現(xiàn)出了它的價(jià)值惕医。
大小端模式
對于多于一個(gè)字節(jié)的數(shù)據(jù)類型,在內(nèi)存中有兩種存儲(chǔ)方式算色,以整型數(shù)0x01234567
為例
大端模式將高位存放在低地址抬伺,而小端模式將高位存放在高地址。這對我們編程有什么影響呢灾梦?說實(shí)話峡钓,在我寫過的程序中并沒考慮過大小端模式,但在如下幾個(gè)方面若河,可以體現(xiàn)出大小端模式的影響:
大端機(jī)器與小端機(jī)器間的網(wǎng)絡(luò)通信能岩。很明顯,如果雙方都遵循先發(fā)低位再發(fā)高位的規(guī)則萧福,發(fā)送前后數(shù)據(jù)在內(nèi)存中的存儲(chǔ)方式是完全一致的拉鹃,但大端機(jī)器和小端機(jī)器對同樣的數(shù)據(jù)存儲(chǔ)方式卻會(huì)解析出完全不同的結(jié)果,因此數(shù)據(jù)出現(xiàn)差錯(cuò)鲫忍。因此膏燕,解決辦法是發(fā)送前全部轉(zhuǎn)換成大端模式,對方接收后悟民,再根據(jù)自己的機(jī)器類型轉(zhuǎn)換成大端或小端模式坝辫。實(shí)際中網(wǎng)絡(luò)通信也的確是這樣做的。
-
反匯編可以看到真實(shí)數(shù)據(jù)順序射亏。
add %eax, 0x8049464
反匯編結(jié)果為
80483bd: 01 05 64 94 04 08
操作數(shù)0x8049464被轉(zhuǎn)換成了小端模式下的結(jié)果64 94 04 08近忙。
繞開類型系統(tǒng)的強(qiáng)制轉(zhuǎn)換。例如如下代碼可以測試出機(jī)器是大端模式還是小端模式:
int value = 0x01234567;
char *ch = (char *)(&value);
if (*ch == 0x01) {
//大端模式
}
else if (*ch == 0x67) {
//小端模式
}
else {
//其它
}
由于ch
一定指向value
最低地址的字節(jié)智润,因此可以通過ch
來判斷系統(tǒng)的大小端類型及舍。
整型數(shù)范圍與C標(biāo)準(zhǔn)
本文最開始就給出了各個(gè)基本數(shù)據(jù)類型的字節(jié)數(shù),根據(jù)字節(jié)數(shù)我們可以推斷出在采用二進(jìn)制補(bǔ)碼編碼的情況下各個(gè)類型的表示范圍窟绷。比如說int
的范圍為-2,147,483,648 ~ 2,147,483,647
锯玛。但是C標(biāo)準(zhǔn)的存在卻提醒我們事實(shí)并非總是如此。
C標(biāo)準(zhǔn)規(guī)定的各類型整型數(shù)的范圍與其典型值有所不同钾麸,通常小于其典型值范圍更振,并且所有有符號類型只要求對稱范圍。例如饭尝,int
只要求-32767 ~ 32767
肯腕。雖然我們使用的編譯器會(huì)按照系統(tǒng)位數(shù)來決定int
類型的實(shí)際范圍,但是钥平,如果想要使代碼能夠跨平臺(tái)实撒,最好按照C標(biāo)準(zhǔn)規(guī)定的范圍來設(shè)計(jì)自己的程序,這樣只要編譯器遵循C標(biāo)準(zhǔn)涉瘾,代碼就一定不會(huì)出錯(cuò)知态。否則,像int i = 99999;
這樣的語句立叛,放到16位計(jì)算機(jī)上就直接溢出了负敏。
接下來提一個(gè)有趣的事情,在32位計(jì)算機(jī)的頭文件<limits.h>
中秘蛇,定義了各個(gè)基本數(shù)據(jù)類型的范圍其做,以整型為例
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX-1)
為什么最小值不直接寫#define INT_MIN -2147483648
呢?
因?yàn)閷幾g器來說赁还,-2147483648是一個(gè)表達(dá)式妖泄,它的意思是對2147483648取負(fù)。但是2147483648這個(gè)數(shù)已經(jīng)超出了整型數(shù)所能表示的范圍艘策,因此這樣寫是沒有意義的蹈胡。更詳細(xì)的解釋可以查看后面的參考資料。
復(fù)合型類型轉(zhuǎn)換——從short到unsigned
數(shù)據(jù)類型既有不同的范圍朋蔫,又有不同的符號性質(zhì)罚渐,當(dāng)兩者同時(shí)轉(zhuǎn)換時(shí),就需要多加注意斑举。
例如搅轿,從short
轉(zhuǎn)換到unsigned
需要分兩步,實(shí)際過程相當(dāng)于(unsigned)(int)
富玷,而不是(unsigned)(unsigned short)
璧坟。也就是說,先擴(kuò)大范圍赎懦,再改變符號性質(zhì)雀鹃。對于下面的代碼
short s = -12345;
unsigned u = s;
printf("%u\n", u);
unsigned u1 = (unsigned)(int) s;
printf("%u\n", u1);
unsigned u2 = (unsigned)(unsigned short) s;
printf("%u\n", u2);
打印結(jié)果u
與u1
都是4294954951
,而u2
是53191
励两。
參考資料
- 詳解大端模式和小端模式 ce123
- ANSI C, Standard C與GCC 思學(xué)
- INT_MIN Jibz
文中若有錯(cuò)誤或不當(dāng)之處黎茎,懇請各位讀者指出。
關(guān)注作者或文集《深入理解計(jì)算機(jī)系統(tǒng)》当悔,第一時(shí)間獲取最新發(fā)布文章傅瞻。