概念
1. Big-Endian(大端模式)
Big-Endian 就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。
2. Little-Endian(小端模式)
Little-Endian 就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
筆記:
大端模式又被稱作大端字節序,高端字節序;小端模式又被稱作小端字節序,低端字節序。(PS:“低端字節序”,我是在學習李忠老師的《x86實模式和保護模式》一書時了解到該詞的)
阮一峰大神的這篇《理解字節序》 對字節序的定義如下:
因為這樣的定義沒有限定場景,因此在不同場景下套用該定義常常會使我混淆,尤其在理解機器指令的時候,這個“誰前誰后”的解釋會讓我無法自洽。
從閱讀習慣看待字節序
我們從小就知道,數字是從左往右讀的,這符合人類的普遍閱讀習慣。
比如十進制數 65535,數位從高到低依次是萬,千,百,十,個。對十進制數而言,最左邊是高位數位,最右邊是低位數位。
我們類比到16進制數 0x12345678
,也從左往右來閱讀這個數。那么,對十六進制數而言,最左邊就是高位字節,最右邊是低位字節。
從內存地址的角度看字節序
首先,你可能需要對內存有一些基本的認識:
- 一個內存單元可以存儲一個字節的內容,因此內存單元也常常被稱為字節單元。
- 一個內存單元可以存儲8個比特,即8個二進制數。但是,如果換算成16進制,一個內存單元僅能容納2個16進制數。
如圖所示,我們在畫內存示意圖的時候,我們用一個綠色矩形表示一個內存單元,每一個內存單元都有一個內存地址,方便計算機的處理器找到這塊內存單元。
另外,我們也習慣於內存地址將低地址端放在下面,高地址端放在上面。
這個習慣,我猜測可能與古人建房子的習慣類似,所謂“萬丈高樓平地起”,最先建的總是低層,然后再建高層。高層究竟建多高,這個總是不斷發展和變化的,但是最底層總是從零開始,這個是相對穩定的。
還是以十六進制數 0x12345678
為例,當它以大端字節序存儲在內存中時,低地址端 0x0000
存儲該數的高位字節 0x12
;高地址端 0x0003
存儲的是該數的低位字節 0x78
。
當 0x12345678
以小端字節序存儲在內存中時,低地址端 0x0000
存儲該數的低位字節 0x78
;高地址端 0x0003
存儲的是該數的高位字節 0x12
。
你看,如果我用內存示意圖的這種上下分布的結構來表示字節序,“誰在前誰在后”這個理論就失效了~
從處理器看字節序
在學習李忠老師的《x86實模式和保護模式》時,我了解到 Intel 8086 處理器(經典的16位處理器)是采用小端字節序。Intel 公司后續的32位x86處理器和64位x64處理器采用的也依然是小端字節序。
比如下面有一條簡單的傳送指令代碼:
mov ax, 0x2000
這條 mov 指令的意思是將立即數 0x2000
傳送到 CPU 內的通用寄存器 ax
中。
這條指令在我配備 Intel x64 處理器的 Windows 系統電腦上,編譯出來的機器碼是 B80020
。
匯編程序編譯出來的文件是二進制文件,它的內容是0或者1,當使用 HexView.exe 用十六進制的方式來查看文件時,就有了如下圖所示:
B8
是機器指令的操作碼,0x2000
的低位字節存儲在二進制文件的低地址端,高位字節存儲在二進制文件的高地址端。
這條指令所在的程序,如果從磁盤中被加載到內存中:
此時,指令在內存中仍然保持着小端字節序存儲。接下來,如果指令執行,就要將內存中的內容放入到通用寄存器 ax
中了。
通常,寄存器的閱讀順序一般是從左到右。左邊是高位字節,右邊是低位字節。
也就是說指令執行之后,最終在寄存器 ax 中顯示的結果和我們源程序中的值是一樣的,都是 0x2000
。
從網絡傳輸的角度看字節序
在網絡上傳輸數據時,由於數據傳輸的兩端對應不同的硬件平台,采用的存儲字節順序可能不一致。所以在TCP/IP協議規定了在網絡上必須采用網絡字節順序,也就是大端模式。
- 對於char型數據只占一個字節,無所謂大端和小端。
- 而對於非char類型數據,必須在數據發送到網絡上之前將其轉換成大端模式。
接收網絡數據時按符合接受主機的環境接收。
簡而言之,網絡字節序是大端字節序。
我們常用 Wireshark 這款軟件抓取 TCP 報文,以下是我抓取到的三次握手中的 SYN
報文的抓包情況:
圖中的 0000, 0010, 0020, 0030, 0040 表示的是16進制數。
我們通常編寫和閱讀的電腦文件中,總是習慣於從上到下,從左到右。此時低地址在左邊或者在上邊,高地址在右邊或者下邊。
十進制數 40190 等於十六進制數 0x9cfe,十進制數 5222 等於十六進制數 0x1466。
我們聚焦抓包的內容的第 4 行,即起始地址為 0x0040
,最后一個地址為 0x004f
。
0x9cfe
的高位字節0x9c
保存在低地址端0x0045
,低位字節是0xfe
保存在高地址端0x0046
。0x1466
的高位字節0x14
保存在低地址端0x0047
,低位字節是0x66
保存在高地址端0x0048
。
總結
可能你不了解內存,不清楚寄存器,不明白編譯,但是你可以記住
“低低低,低高高”
-
低位字節排放在內存的低地址端,就是低端字節序。
-
低位字節排放在內存的高地址端,就是高端字節序。
-
高端字節序就是大端字節序;低端字節序就是小端字節序。