整型在記憶體中的儲存
1.整型的歸類
- char
- short
- int
- long
以上都分為有符號(signed)與無符號(unsigned)的型別
2.原碼、反碼和補碼
2.1 定義
計算機在表示一個數字時,是採用二進位制的方式,所以為了準確表示一個數的正負,每一個有符號數都將其最高位視作是符號位,最高位為0表示正數,最高位為1表示負數。我們接下來以有符號整型int的數字進行分析。
一個有符號整數由符號位+數值位組成,數值位是其最高位,分別以0/1表示正/負
對於正數來說,反碼補碼都與原碼相同;
對於負數來說,符合以下3條規則:
- 原碼:將十進位制數字直接翻譯為二進位制數
- 反碼:原碼的符號位不變,其他位按位取反
- 補碼:反碼+1
而對於整型來說,整型在記憶體中實際上是以補碼的形式進行儲存的。
2.2 補碼的意義
有的同學可能就會問了,為什麼計算機要發展出原碼、反碼、補碼這麼多種碼呢?
這就與計算機對於整數的運算有關了。
CPU只有加法器,減法在運算時也會被視作一個數加另一個負數。考慮到整數的最高位是符號位,兩個整數中若包含負數,以原碼直接相加得到的數一定是不對的。所以問題就變成了如何使得運算簡單而精確,既要處理符號位,又要只進行加法運算,達到以某一種二進位制形式的“碼”直接相加就能得到正確結果。
下面,我們以60+(-18)為例,分別用原碼、反碼、補碼直接進行二進位制的運算。
原碼運算
00000000 00000000 00000000 00111100( 60的原碼)
+ 10000000 00000000 00000000 00010010(-18的原碼)
-------------------------------------------
10000000 00000000 00000000 01001110(某個數的原碼)
顯然,得到了的原碼轉化為10進位制是-78,並非正確答案42。
反碼運算
00000000 00000000 00000000 00111100( 60的反碼)
+ 11111111 11111111 11111111 11101101(-18的反碼)
-------------------------------------------
100000000 00000000 00000000 00101001
擷取後32位:
00000000 00000000 00000000 00101001(某個數的反碼)
顯然,得到了的反碼轉化為10進位制原碼是41,並非正確答案42,但是隻與正確答案相差(+1),於是,我們就想將負數的反碼+1,即變成“補碼”來進行運算,而又正數的補碼是原碼本身,這時候我們看看會怎麼樣呢?
補碼運算
00000000 00000000 00000000 00111100( 60的補碼)
+ 11111111 11111111 11111111 11101110(-18的反碼)
-------------------------------------------
100000000 00000000 00000000 00101010
擷取後32位:
00000000 00000000 00000000 00101010(某個數的補碼)
顯然,得到了的補碼轉化為10進位制原碼是42,我們得到了正確結果。
2.3 結論
綜上,我們發現,只要將兩個整數使用補碼進行運算,就不需要考慮它們的符號位了,將它們的所有位直接簡單相加即可,就能得到正確的結果。
2.4* 負數二進位制補碼的快速轉化
對於char型別整數,-1用二進位制補碼錶示為
11111111
當我們已知一個負數的二進位制補碼時,用比這個數多一位的、最高位為1、其他位全0、這裡應為9位的二進位制數
100000000
直接減去-1的二進位制補碼得
00000001
得到的數就是十進位制(-1)的絕對值,也就是1,只要加上負號,就能快速得到這個負數二進位制補碼的十進位制原碼。
原理十分簡單,一個負數的原碼加上補碼 =原碼+反碼+1 = 所有二進位制位全1再加1 = 多一位的、最高位為1、其他位全0
3. 大小端位元組序
3.1 什麼是大小端
在記憶體中,資料的大小端儲存是在位元組尺度上進行討論的
大端儲存模式:資料的低位儲存在記憶體的高地址,資料的高位儲存在記憶體的低地址
小端儲存模式:資料的低位儲存在記憶體的低地址,資料的高位儲存在記憶體的高地址
3.2 為什麼有大端和小端之分
在計算機系統中,我們通常是以位元組為單位儲存資料的,每個地址對應一個位元組。
一個位元組為8bit,但是在C語言中除了8bit的char之外,還有16bit的short,32bit的int。另外,對於位數大於8位的處理器,例如16位和32位的處理器,由於暫存器寬度大於一個位元組,那麼必然存在著如何將多個位元組安排的問題。這邊導致了大小端儲存模式的誕生。
我們以int型別的數0x01ff4218為例(兩個十六進位制位即為1個位元組),看一下在大小端下這4個位元組分別是如何分配的
- 大端儲存模式
- 小端儲存模式
3.3 寫一段程式碼來判斷你的機器的大小端位元組序
演算法簡單概括:擷取4個位元組大小的int整型的1個位元組的低位。若機器為大端位元組序,該位元組儲存0x00;若機器為小端位元組序,該位元組儲存0x01;
#include<stdio.h>
//實現方法1
int check1()
{
int i = 1;
return *(char*)&i;
}
//實現方法2
int check2()
{
union check
{
int i;
char c;
}ch = {1};
return ch.c;
}
int main()
{
int ret = check1();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
4.參考文獻
- C Primer Plus, 第六版, p494