從零開始給女朋友講計算機 1 – 從比特、位元組、補碼到 ASCII、GB2312、Unicode
起因
在代碼 review 的過程中,總是發現有人在數據類型轉換(reinterpret_cast)、大小端等問題(什麼情況下需要考慮大小端,什麼情況下不需要考慮)上犯錯誤,究其原因是沒有徹徹底底地搞懂數據的二進制表示。我想寫篇文章,用通俗易懂的語言把這件事情說明白,通俗易懂到我希望我的小白女友也能看懂。於是我就嘗試着先做些鋪墊,給她講了些基礎。發現效果出奇的好,於是趕緊把這一過程記錄如下。
0 和 1 的世界
計算機的世界只有 0 和 1,所有的數據都由 0 和 1 的組合:數字、字母、漢字、圖片、音樂、電影、遊戲、網頁等都可以由很多的 0 和 1 組成。
計算機如何知道一長串的 0 和 1 是什麼含義呢?
比如 0100 0001 可能表示數字 65,可能表示大寫字母A,可能和更多的 0 和 1 共同組成一個漢字,也可能表示圖片上某個點的顏色,其意義完全取決於人們約定的規則。
比特和位元組
正着說:每一個 0 和 1 叫做一個比特(bit),8 個比特組成一個位元組(Byte)。位元組是計算機的基本單位,通常計算機一次最少處理一個位元組。
例如:人們常說的一個 Word 文檔 100 KB,一張圖片 2 MB,一首歌 10 MB,一部電影 4 GB,內存 8 GB,硬盤 512 GB 等等。這裡的大「B」就是 Bytes,位元組。
比特(bit)最常見於寬帶的宣傳:例如 500M 寬帶的完整單位是 500 Mbps(注意這裡是小「b」,不是大「B」)。bps 即 bits per second,500Mbps 指的是每秒最大傳輸 500 兆比特(bit)。所以 500M 的寬帶最快下載速度不是 500 MB/s,而是 500/8 = 62.5 MB/s。
反着再說一次:一個位元組(byte)有 8 個比特(bit);每個比特只能是 0 或 1,8 個比特一共有 2^8 = 256 種組合,可以代表 256 種含義(具體含義完全取決於人們約定的規則)。
數字的二進制表示:用 0 和 1 表示數字
首先想到用 8 個比特表示 0-255:人們約定,高位到低位每個 bit 有不同的權重,代表不同的值,如此便可用 8 個 bit 表示 0-255 的所有數字。
| 高位 -> 低位 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
|---|---|---|---|---|---|---|---|---|
| 權重 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 舉例:0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:35 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 |
| 舉例:65 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 舉例:128 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:255 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
對於這種不考慮負數的情況,我們稱之為無符號數。
那如何表示一個負數(有符號數)?
有很多種方法,只要約定好一個規則即可。比如我們可以約定,最高位 bit7 代表符號位,0 代表正數,1 代表負數。於是一個位元組,8 個 bit 可以表示 -127 ~ 127 的數字。注意其中 0 有兩種表示,+0 和 -0。
| 高位 -> 低位 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
|---|---|---|---|---|---|---|---|---|
| 權重 | +/- | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 舉例:+0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:-0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:35 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 |
| 舉例:-65 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 舉例:127 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 舉例:-127 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
現實計算機世界的負數幾乎都是補碼表示。和無符號數的規則相比,差別僅在最高位的權重為負。於是一個位元組,8 個 bit 可以表示 -128 ~ 127 的數字。其中 0 只有一種表示。
| 高位 -> 低位 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
|---|---|---|---|---|---|---|---|---|
| 權重 | -128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 舉例:0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:35 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 |
| 舉例:65 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 舉例:-128 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:127 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 舉例:-127 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 舉例:-1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
先停一下
看到這裡,如果問你,1 0 0 0 0 0 0 0 代表一個什麼數字,你要怎麼回答?千萬別急着回答,回答之前應該先問清楚,要按照什麼規則去解析。比如這串 0/1 表示的是一個無符號數還是一個補碼錶示的有符號數。
如何表示更大的數?比如 10000
用多個位元組表示。一個位元組不夠就兩個,兩個不夠就三個、四個甚至八個十六個,直到夠用!用 2 個位元組就能夠表示 0 – 65535 之間的無符號數,用 4 個位元組就能表示 0 – 4294967295 的無符號數!
| 高位 -> 低位 | bit 15 | bit 14 | bit 13 | bit 12 | bit 11 | bit 10 | bit 9 | bit 8 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 權重 | 32768 | 16384 | 8192 | 4096 | 2048 | 1024 | 512 | 256 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 舉例:0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:65 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 舉例:255 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 舉例:10000 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 舉例:40256 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:60666 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
有符號數(補碼)也是類似的,只不過最高位的權重為負。用 2 個位元組就能夠表示 -32768 到 32767 之間的有符號數,用 4 個位元組就能表示 -2147483648 到 2147483647 的有符號數!
直接使用上面的表格(二進制表示的 bit 15 到 bit 0 和上面一模一樣),但是現在按照補碼的規則進行解析(最高位權重為負),於是得到的結果就不一樣了。
| 高位 -> 低位 | bit 15 | bit 14 | bit 13 | bit 12 | bit 11 | bit 10 | bit 9 | bit 8 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 權重 | -32768 | 16384 | 8192 | 4096 | 2048 | 1024 | 512 | 256 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 舉例:0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:65 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 舉例:255 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 舉例:-25280 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 舉例:-4870 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
十六進制:二進制的簡化表示法
二進制要用 8 個 0/1 表示一個 byte,太不方便,為簡化表示,十六進制用分別用一個 0-F 表示一個位元組的前 4 位和後 4 位。一般還會加上前綴0x,以提醒讀者後面是 16 進制表示法。
如何表示帶小數點的數(浮點數)?
還是一樣,只要約定好一個規則就行。計算機界流行的浮點數規則是 IEEE 定義單精度浮點(4 位元組表示)和雙精度浮點(8 位元組表示)。具體規則比較複雜,不在這裡展開。
如何表示字符?
我們可以約定,0000 0001 代表 a,0000 0002 代表 b,以此類推。從 0000 0000 到 1111 1111 的 256 種組合中表示 a-z、A-Z,加上各種標點符號也是綽綽有餘。現實計算機世界幾乎都心照不宣地採用 ASCII 規則來表示常見的英文字符、標點以及一些不顯示的控制字符等。ASCII 只用了 7 個 bit。
| 進制 | 十進制 | 十六進制 | 字符/縮寫 | 解釋 |
|---|---|---|---|---|
| 00000000 | 0 | 00 | NUL (NULL) | 空字符 |
| 00001010 | 10 | 0A | LF/NL(Line Feed/New Line) | 換行鍵 |
| 00001101 | 13 | 0D | CR (Carriage Return) | 回車鍵 |
| 00100000 | 32 | 20 | (Space) | 空格 |
| 00100001 | 33 | 21 | ! | |
| 00101100 | 44 | 2C | , | |
| 00101110 | 46 | 2E | . | |
| 00110000 | 48 | 30 | 0 | |
| 00110001 | 49 | 31 | 1 | |
| 00110010 | 50 | 32 | 2 | |
| 01000000 | 64 | 40 | @ | |
| 01000001 | 65 | 41 | A | |
| 01000010 | 66 | 42 | B | |
| 01000011 | 67 | 43 | C | |
| 01011000 | 88 | 58 | X | |
| 01011001 | 89 | 59 | Y | |
| 01011010 | 90 | 5A | Z | |
| 01100001 | 97 | 61 | a | |
| 01100010 | 98 | 62 | b | |
| 01100011 | 99 | 63 | c | |
| 01111000 | 120 | 78 | x | |
| 01111001 | 121 | 79 | y | |
| 01111010 | 122 | 7A | z | |
| 01111111 | 127 | 7F | DEL (Delete) | 刪除 |
如何表示漢字?
一個位元組一共就 256 種排列組合,就算每個組合代表一個漢字,也只能表示 256 個漢字,這顯然是不夠的。要想表示一個漢字,至少需要 2 個位元組。這樣就有 2^16 = 65536 種排列組合,可以表示 65536 個漢字了。應對常見的漢字已經不成問題。GB2312 編碼就是用兩個位元組給漢字編碼的。具體編碼規則網上找得到,這裡不詳細展開。
如何表示韓文、日文、阿拉伯文等所有字符?
每個國家、地區都有自己的編碼方式。比如同樣的一串數字 1011 0000 1010 0001 在GB2312 編碼下代表漢字「啊」,而在某種日文編碼規則中則可能代表一個日文字符。
比如一個日本程序員開發了一個軟件,在日文編碼的機器上可以正常顯示日文,但是如果拿到中文編碼的機器上就會顯示亂碼。為解決這一問題,推出了 Unicode 編碼。Unicode 採用 4 位元組編碼,可以表示 2^32 = 4294967295 個字符,足夠容納目前世界上所有已知的字符了,甚至包括各種 emoji 表情!
總結
計算機的世界由 0/1 組成,數字、字母、圖片等等所有信息都由一串串的 0/1 表示。8 個比特組成一個位元組,位元組是計算機的基本單位。一個位元組可以表示 2^8 = 256 種含義,如何解析完全取決於人們約定的規則。如果一個位元組不足以表示所有的範圍、可能性,就用多個位元組表示。


