1. 程式人生 > >位元組序(大小端)詳解從高低地址和高低位開始理解(轉)

位元組序(大小端)詳解從高低地址和高低位開始理解(轉)

一、位元組序定義

位元組序,顧名思義位元組的順序,再多說兩句就是大於一個位元組型別的資料在記憶體中的存放順序(一個位元組的資料當然就無需談順序的問題了)。

其實大部分人在實際的開發中都很少會直接和位元組序打交道。唯有在跨平臺以及網路程式中位元組序才是一個應該被考慮的問題。

在所有的介紹位元組序的文章中都會提到位元組序分為兩類:Big-Endian和Little-Endian。引用標準的Big-Endian和Little-Endian的定義如下:
a) Little-Endian就是低位位元組排放在記憶體的低地址端,高位位元組排放在記憶體的高地址端。
b) Big-Endian就是高位位元組排放在記憶體的低地址端,低位位元組排放在記憶體的高地址端。
c) 網路位元組序:4個位元組的32 bit值以下面的次序傳輸:首先是0~7bit,其次8~15bit,然後16~23bit,最後是24~31bit。這種傳輸次序稱作大端位元組序。由於 TCP/IP首部中所有的二進位制整數在網路中傳輸時都要求以這種次序,因此它又稱作網路位元組序。比如,乙太網頭部中2位元組的“乙太網幀型別”,表示後面資料的型別。對於ARP請求或應答的乙太網幀型別來說,在網路傳輸時,傳送的順序是0x08,0x06。在記憶體中的映象如下圖所示:
棧底 (高地址)
---------------
0x06 -- 低位 
0x08 -- 高位
---------------
棧頂 (低地址)
該欄位的值為0x0806。按照大端方式存放在記憶體中。

二、高/低地址與高低位元組

首先我們要知道我們C程式映像中記憶體的空間佈局情況:在《C專家程式設計》中或者《Unix環境高階程式設計》中有關於記憶體空間佈局情況的說明,大致如下圖:
----------------------- 最高記憶體地址 0xffffffff
 | 棧底
 .
 .              棧
 .
  棧頂
-----------------------
 |
 |
\|/

NULL (空洞)

/|\
 |
 |
-----------------------
                堆
-----------------------
未初始化的資料
----------------(統稱資料段)
初始化的資料
-----------------------
正文段(程式碼段)
----------------------- 最低記憶體地址 0x00000000

以上圖為例如果我們在棧上分配一個unsigned char buf[4],那麼這個陣列變數在棧上是如何佈局的呢[注1]?看下圖:
棧底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
棧頂 (低地址)

現在我們弄清了高低地址,接著來弄清高/低位元組,如果我們有一個32位無符號整型0x12345678(呵呵,恰好是把上面的那4個位元組buf看成一個整型),那麼高位是什麼,低位又是什麼呢?其實很簡單。在十進位制中我們都說靠左邊的是高位,靠右邊的是低位,在其他進位制也是如此。就拿 0x12345678來說,從高位到低位的位元組依次是0x12、0x34、0x56和0x78。

高低地址和高低位元組都弄清了。我們再來回顧一下Big-Endian和Little-Endian的定義,並用圖示說明兩種位元組序:
以unsigned int value = 0x12345678為例,分別看看在兩種位元組序下其儲存情況,我們可以用unsigned char buf[4]來表示value:
Big-Endian: 低地址存放高位,如下圖:
棧底 (高地址)
---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
棧頂 (低地址)

Little-Endian: 低地址存放低位,如下圖:
棧底 (高地址)
---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
---------------
棧頂 (低地址)

在現有的平臺上Intel的X86採用的是Little-Endian,而像Sun的SPARC採用的就是Big-Endian。

三、例子

嵌入式系統開發者應該對Little-endian和Big-endian模式非常瞭解。採用Little-endian模式的CPU對運算元的存放方式是從低位元組到高位元組,而Big-endian模式對運算元的存放方式是從高位元組到低位元組。

例如,16bit寬的數0x1234在Little-endian模式CPU記憶體中的存放方式(假設從地址0x4000開始存放)為:

記憶體地址  存放內容
 0x4001    0x12
 0x4000    0x34

而在Big-endian模式CPU記憶體中的存放方式則為:

記憶體地址  存放內容
 0x4001    0x34
 0x4000    0x12
 
32bit寬的數0x12345678在Little-endian模式CPU記憶體中的存放方式(假設從地址0x4000開始存放)為:

記憶體地址  存放內容
 0x4003     0x12
 0x4002     0x34
 0x4001     0x56
 0x4000     0x78
 
而在Big-endian模式CPU記憶體中的存放方式則為:

記憶體地址  存放內容
 0x4003     0x78
 0x4002     0x56
 0x4001     0x34
 0x4000     0x12