記憶體的最小單位是位元組,一個位元組等於8位(bit),每一位要麼是0要麼是1,也就是用二進位制來表示。
一個位元組在記憶體中的表示為:
無符號整數的表示
無符號二進位制轉成十進位制公式:
w
:二進位制位的長度。i
:二進位制位從右往左開始的下標,從0開始計數。w-1
:由於i是從0開始計數,所以最後一個下標就是w-1。x(i)
:第i
位的值,要麼是0要麼是1。2^i
:2的第i
次冪。
例如:
無符號二進位制數10010
按照公式展開就是:
如果把這個數用1個位元組在計算機中儲存,記憶體中就表示為:
不足8位,左邊補0。
1個位元組的無符號正式能表示2^8 = 256
個不同的數。能表示最大的數是8個二進位全是1的數等於255,也就是求一個公比為2
,首項是1
的等比數列
前8項和。二進位制位求和公式為(2^n) - 1
。總結下來一個n位的二進位制數能表示最大的數是(2^n) - 1
,能夠表示2^n
個不同的數,之所以是2^n個不同的數,是因為可以表示0~(2^n) - 1
,從0開始的所以還需要+1
個長度。
Char在記憶體中的表示
Char型別是用來儲存單個字元,在記憶體中佔用1個位元組的大小,它使用8個bit來表示256個字元。
Char型別實際儲存的是字元的ASCII
碼,由於ASCII
碼是整數。所以Char最終在記憶體中是一個8bit的整型。
比如字元A
的ASCII
碼是65,65 = 2^0 + 2^6,所以在記憶體中的表示為:
char ch = 'A';
printf("%d", ch); // output is 65
Short在記憶體中的表示
Short 表示的是短整型,一般佔用2個位元組的記憶體大小。
它的取值範圍是(-2)^15~(2^15)-1
包含0。最大值這裡是(2^15)-1
,是因為short有符號位,需要用最高位(用從左到右第一位)來表示符號,0表示正數,1表示負數。 最大值的二進位制表示為0111111111111111
(16個二進位制位),十進位制就是(2^15)-1
。 之所以是(2^15)-1
,也是之前說的求和公式((2^n)-1
。
實現加減法
二進位制加減法和十進位制一樣,把對應
位
相加,大於1就向前進位。例如0111 + 1 = 1000
如果想要把7和-7相加使結果等於0。按照在計算機中使用二進位制的最高位來當做符號位的,0表示正數,1表示負數。那麼7表示為0000111
,-7就表示為1000111
。0000111 + 1000111 按照二進位制先前的加法法則得出來是1001110
,結果不是我們想要的0。
怎麼才能讓2個二進位制數相加得到0呢?
想要得到0,就需要利用進位,比如在11111111
(8個1)的基礎上加1就可以得到100000000
(一共9位,左邊第一位是1,後面8個0) ,舍掉最左邊的那個1就得到了8個0最終結果就等於0。把原碼按位取反然後與原碼相加就可以得到全1的二進位制數。比如0000111
按位取反就是1111000
,他們倆相加得到11111111
。 再把它加1就得到最後的結果0。整個過程需要3步,我們把最後兩步合併成一個步驟,也就是把按位取反和加1合併到一起,其實就是把原碼的反碼加1。如1111000
加1得到1111001
。最後這兩步合在一起叫做取原碼的補碼。最後得到的1111001
就叫做0000111
的補碼。
- 正整數的補碼是其本身。
- 負整數的補碼是把它對應的正整數二進位制碼按位取反,也就做原碼的反碼然後再加1。
比如正整數7
的二進位制碼是0000111
,它的補碼還是它本身。再比如-7
對應的正整數二進位制碼是0000111
,它的反碼就是1111000
(把原碼按位取反)。然後再加1
就得到1111001
。1111001
就是-7
的補碼。我們再次把1111001
和0000111
按照二進位制加法法則相加剛好得到0。這裡需要注意的是,這裡左邊會產生一個溢位位,這個溢位位是去掉不要的,得到結果就是0。
-1的補碼全是1,因為它加上1之後就變成了0。
計算機系統都是用補碼來表示二進位制碼,這樣的好處之一就是可以讓加減法運算統一處理。
位模式拷貝
當把
char
型別的變數賦值給short
型別的變數時,會把char
的8個bit放在short
的低八位(從右往左第一個位元組)上。
例如:
char ch = 'A'; // 'A' ASCII:65 記憶體表示為 01000001
short s = ch; // 記憶體表示為 00000000 | 01000001
一個特殊的情況就是當把一個short
的-1
賦值給一個int
變數的時候,並不會得到00000000 | 00000000 | 11111111 | 11111111
,因為如果這樣的話表示的值就不是-1
了。所以正確的做法就是把所有的1
全部拷貝給int
。
例如:
short s = -1; // 記憶體表示為 11111111 | 11111111
int i = s; // 記憶體表示為 11111111 | 11111111 | 11111111 | 11111111
相反如果把short
型別的變數賦值給char
型別的變數時,會把short
的低八位(從右往左第一個位元組)放在char
僅有的一個位元組上。會把多的位元組自動剔除。
例如:
short s = 65; // 記憶體表示為 00000001 | 01000001
char ch = s; // 記憶體表示為 01000001
浮點數的表示
我們已經知道無符號二進位制轉成十進位制公式為:
這裡的i
是從0開始的也就是從右邊的第一位是2^0
,如果我們從一個負整數開始的話,就會存在負整數次冪,那麼也就會出現小數部分了。
例如有一個16位的二進位制數000000011 | 11000000
用它的前八位來表示整數部分,後八位來表示小數部分,就也可以這樣表示000000011.11000000
。這樣後八位也就不再是整數次冪了,而是從左到右每一位分別是2^(-1)~2^(-8)
。這個數就可以表示成:
這是其中一種浮點數表示方法,這種方法表示的浮點數會出現精度不夠,表示的數值區間比較小,所以計算機實際並沒有用該方法來表示浮點數。
下面這種方法就是計算機內部真實表示浮點數的方法。
我們先來看下十進位制的科學計數法,用科學計數法表示123.45的話就是1.2345 * 10^2
。其中1.2345 為尾數
,10為基數
,2為指數
。計算機在表示浮點數的時候,也借用了十進位制的科學計數法的思想,只不過基數為2
了。
例如1000.01
可以表示成1.00001 * 2^3
,幾次冪,小數點就向右移動幾位。
用32位
的float
來舉例,首位是符號位S
,緊跟後面8位
是指數位E
,最後23位
稱為尾數位M
。
計算公式:
-
S:符號位
S為0時剛好是正數,為1時是負數。
-
M:尾數部分
它的取值範圍是
1≤M<2
,取值方式是從左到右每一位分別表示的是2^-1~2^-23
,值就是然後對各個位的表示值求和,這裡跟先前浮點數表示的辦法一致,都是從負整數次冪開始。由於尾數的整數部分始終都是1
,所以這個1
可以被省略,這樣就可以多出一位來提升精度。 -
E:指數部分
減去127是因為偏移量是127。
例如0 | 10000010 | 11110000000000000000000
的每一部分別是:
- S:
0
表示整數。
- M:
11110000000000000000000
這裡需要再加1,因為為了提升小數精度省略了1,所以要加回來。所以完整的尾數部分應該是
1.1111
(省略了後面的0)。 2^0 + 2^-1 + 2^-2 + 2^-3 + 2^-4 = 1.9375 - E:
10000010
2^7 + 2^1 = 130
分別帶入公式得:
二進位制形式:
十進位制形式:
詳細過程:1 * 1.1111 * 2^(130-127)
=> 1 * 1.1111 * 2^3
=> 1 * 1111.1
(幾次冪,小數點就向右移動幾位) => 1 * (2^3 + 2^2 + 2^1 + 2^0 + 2^-1)
=> 1 * (8 + 4 + 2 + 1 + 0.5)
=> 15.5
浮點數與整數相互賦值
當我們在把浮點數與整數相互賦值的時候,並不會直接拷貝bit位,而是重新計算出在新的型別中的位模式。
例如:
int i = 5; // 記憶體表示 00000000 | 00000000 | 00000000 | 00000101
// 重新計算5在float中的表示方式
float f = i; // 記憶體表示 0 | 00000000 | 00000000000000000000101
printf("%f", f) // output is 5.0
來一點更刺激的!!!
// 2^30
int i = 1073741824; // 記憶體表示 01000000 | 00000000 | 00000000 | 00000000
// 這裡就不會重新計算在float中的表示方式了,而是直接把bit位拷貝過去。用float的解析方式去解析int的那塊記憶體。
float f = *(float *)&i; // 記憶體表示 0 | 10000000 |00000000000000000000000
// 1 * 2^(128-127) * 1 = 2
printf("%f", f) // output is 2.0
這裡就不會重新計算1073741824
在float中的表示方式了,而是直接把int
的bit位拷貝過去
。用float的解析方式去解析int的那塊記憶體
。