一、引子
在各種電腦體繫結構中,對於位元組、字等的儲存機制有所不同,因而引發了電腦通訊領域中一個很重要的問題,即通訊雙方交流的資訊單元(位元、位元組、字、雙字等等)應該以什麼樣的順序進行傳送。如果不達成一致的規則,通訊雙方將無法進行正確的編/解碼從而導致通訊失敗。目前在各種體系的電腦中通常採用的位元組儲存機制主要有兩種:
big-edian和little-endian。
本文簡要描述這兩種儲存機制的來曆、特點和區別。
為了敘述方便,下面先對本文中將要用到的兩個術語做簡單的定義。 1、MSB MSB是Most Significant Bit/Byte的首字母縮寫,通常譯為最重要的位或者最重要的位元組。它通常用來表明在一個bit序列(如一個byte是8個bit組成的一個序列)或者一個byte序列(如word是兩個byte組成的一個序列)中對整個序列取值影響最大的那個bit/byte。 2、LSB LSB是Least Significant Bit/Byte的首字母縮寫,通常譯為最不重要的位或者最不重要的位元組。它通常用來表明在一個bit序列(如一個byte是8個bit組成的一個序列)或者一個byte序列(如word是兩個byte組成的一個序列)中對整個序列取值影響最小的那個bit/byte。
二、endian的由來 1、Definition endian: The ordering of bytes in a multi-byte number. 定義:在電腦系統體繫結構中用來描述在多位元組數中各個位元組的儲存順序。
2、Etymology The term comes from Swift's "Gulliver's Travels" via the famous paper"On Holy Wars and a Plea for Peace" by Danny Cohen, USC/ISI IEN 137,1980-04-01. The Lilliputians, being very small, had correspondingly small political problems. The Big-Endian and Little-Endian parties debated over whether soft-boiled eggs should be opened at the big end or the little end.[From: Free On-Line Dictionary Of Computing or Jargon File] 詞源:據Jargon File記載,endian這個詞來源於JonathanSwift在1726年寫的諷刺小說"Gulliver's Travels"(《格利佛遊記》)。該小說在描述Gulliver暢遊小人國時碰到了如下的一個情境。在小人國裡的小人因為非常小(身高6英寸)所以總是碰到一些意想不到的問題。
有一次因為對水煮蛋該從大的一端(Big-End)剝開還是小的一端(Little-End)剝開的爭論而引發了一場戰爭,並形成了兩支截然對立的隊伍:支援從Big-End剝開的人Swift就稱作Big-Endians而支援從Little-End剝開的人就稱作Little-Endians……(尾碼ian表明的就是支援某種觀點的人:-)。Endian這個詞由此而來。 1980年,Danny Cohen在其著名的論文"On Holy Wars and a Plea for Peace"中為了平息一場關於在訊息中位元組該以什麼樣的順序進行傳送的爭論而引用了該詞。
該文中,Cohen非常形象貼切地把支援從一個訊息序列的MSB開始傳送的那夥人叫做Big-Endians,支援從LSB開始傳送的相對應地叫做Little-Endians。此後Endian這個詞便隨著這篇論文而被廣為採用。
三、各種endian 1、big-endian A computer architecture in which, within a given multi-byte numeric representation, the most significant byte has the lowest address (the word is stored "big-end-first").
Most processors, including the IBM 370 family, the PDP-10, theMotorola microprocessor families, and most of the various RISC designs current in mid-1993, are big-endian.
[From: Free On-Line Dictionary Of Computing or Jargon File] big-endian:電腦體繫結構中一種描述多位元組儲存順序的術語,在這種機制中最重要位元組(MSB)存放在最低端的地址上。採用這種機制的處理器有IBM3700系列、PDP-10、Mortolora微處理器系列和絕大多數的RISC處理器。
+--------------------------+
| 0x34 |<-- 0x00000021
+--------------------------+
| 0x12 |<-- 0x00000020
+--------------------------+
圖1:雙位元組數0x1234以big-endian的方式存在起始地址0x00000020中
在Big-Endian中,對於bit序列中的序號編排方式如下(以雙位元組數0x8B8A為例):
bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+------------------------------------------------+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+-------------------------------------------------+
^ 0x8B 0x8A ^
MSB LSB
圖2:Big-Endian的bit序列編碼方式
注1:通常在TCP/IP協議棧所說的網路序(Network Order)就是遵循Big-Endian 規則。在TCP/IP網路通訊中,通訊雙方把訊息按照2的方式進行編碼,然後按從MSB(Bit0)到LSB的順序在網路上傳送。 2、little-endian A computer architecture in which, within a given 16- or 32-bit word,bytes at lower addresses have lower significance (the word is stored "little-end-first"). The PDP-11 and VAX families of computers and Intel microprocessors and a lot of communications and networking hardware are little-endian. The term is sometimes used to describe the ordering of units other than bytes; most often, bits within a byte.
[From: Free On-Line Dictionary Of Computing or Jargon File] little-endian:電腦體繫結構中一種描述多位元組儲存順序的術語,在這種機制中最不重要位元組(LSB)存放在最低端的地址上。採用這種機制的處理器有PDP-11、VAX、Intel系列微處理器和一些網路通訊裝置。該術語除了描述多位元組儲存順序外還常常用來描述一個位元組中各個位元的排放次序。
+--------------------------+
| 0x12 |<-- 0x00000021
+---------------------------+
| 0x34 |<-- 0x00000020
+---------------------------+
圖3:雙位元組數0x1234以little-endian的方式存在起始地址0x00000020中
在Little-Endian中,對於bit序列中的序號編排和Big-Endian剛好相反,其方式如下(以雙位元組數0x8B8A為例):
bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+-----------------------------------------+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+-----------------------------------------+
^ 0x8B 0x8A ^
MSB LSB
圖4:Little-Endian的bit序列編碼方式
注2:通常我們說的主機序(Host Order)就是遵循Little-Endian規則。所以當兩台主機之間要通過TCP/IP協議進行通訊的時候就需要調用相應的函數進行主機序(Little-Endian)和網路序(Big-Endian)的轉換。
注3:正因為這兩種機制對於同一bit序列的序號編排方式恰恰相反,所以《現代英漢詞典》中對MSB的翻譯為“最高有效位”欠妥,故本文定義為“最重要的bit/byte”。
3、middle-endian: Neither big-endian nor little-endian. Used of perverse byte orders such as 3-4-1-2 or 2-1-4-3, occasionally found in the packed decimal formats of some minicomputer manufacturers.
[From: Free On-Line Dictionary Of Computing or Jargon File] middle-endian:除了big-endian和little-endian之外的多位元組儲存順序就是middle-endian,比如以4個位元組為例:象以3-4-1-2或者2-1-4-3這樣的順序儲存的就是middle-endian。這種儲存順序偶爾會在一些小型機體系中的十進位數的壓縮格式中出現。
四、收尾 要詳細解釋這兩種編碼順序已經超出本文所涉及的內容,如果你有興趣的話可以參考上面提及的Danny Cohen的論文("On Holy Wars and a Plea for Peace"),該論文詳細的描述了這兩種編碼順序的曆史、所基於的數學理論和各自擁護者爭論的焦點等知識,絕對可以大飽你打破沙鍋問到底的內心需要。
什麼是位元組序? 位元組序,顧名思義位元組的順序,再多說兩句就是大於一個位元組類型的資料在記憶體中的存放順序(一個位元組的資料當然就無需談順序的問題了)。其實大部分人在實際的開發中都很少會直接和位元組序打交道。唯有在跨平台以及網路程式中位元組序才是一個應該被考慮的問題。在所有的介紹位元組序的文章中都會提到位元組序分為兩類:Big-Endian和Little-Endian。引用標準的Big-Endian和Little-Endian的定義如下:
a) Little-Endian就是低位位元組排放在記憶體的低地址端,高位位元組排放在記憶體的高地址端。
b) Big-Endian就是高位位元組排放在記憶體的低地址端,低位位元組排放在記憶體的高地址端。c) 網路位元組序:TCP/IP各層協議將位元組序定義為Big-Endian,因此TCP/IP協議中使用的位元組序通常稱之為網路位元組序。
PS:有些文章中稱低位位元組為最低有效位,高位位元組為最高有效位。
Big endian means that the most significant byte of any multibyte data field is stored at the lowest memory address, which is also the address of the larger field.
Little endian means that the least significant byte of any multibyte data field is stored at the lowest memory address, which is also the address of the larger field.
什麼是高/低地址端什麼是高/低位元組 首先我們要知道我們C程式映像中記憶體的空間布局情況:在《C專家編程》中或者《Unix環境進階編程》中有關於記憶體空間布局情況的說明,大致如:
--------------最高記憶體位址 0xffffffff -------------
棧底
棧…
棧頂
-------------------------------------------------------
NULL (空洞)
--------------------------------------------------------
堆------------------------------------------------------
未初始化的資料
--------------------------------------(統稱資料區段)
初始化的資料
-------------------------------------------------------
本文段(程式碼片段)
---------------最低記憶體位址 0x00000000--------
以為例如果我們在棧上分配一個unsigned char buf[4],那麼這個陣列變數在棧上是如何布局的呢?看:
------棧底(高地址)-----
buf[3]
buf[2]
buf[1]
buf[0]
----棧頂(低地址)------
現在我們弄清了高/低地址,接著考慮高/低位元組。如果我們有一個32位無符號整型0x12345678,那麼高位是什麼,低位又是什麼呢?其實很簡單。在十進位中我們都說靠左邊
的是高位,靠右邊的是低位,在其他進位也是如此。就拿 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。那麼在跨平台或網路程式中如何?位元組序的轉換呢?這個通過C語言的移位操作很容易實現,例如下面的宏:
#if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)
#define htons(A) (A)
#define htonl(A) (A)
#define ntohs(A) (A)
#define ntohl(A) (A)
#elseif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
#define htons(A) ((((uint16)(A) & 0xff00) >> 8) | (((uint16)(A) & 0x00ff) << 8))
#define htonl(A) ((((uint32)(A) & 0xff000000) >> 24) |
/ (((uint32)(A) & 0x00ff0000) >> 8) |
/ (((uint32)(A) & 0x0000ff00) << 8) |
/ (((uint32)(A) & 0x000000ff) << 24))
#define ntohs htons
#define ntohl htohl
#else
#error "Either BIG_ENDIAN or LITTLE_ENDIAN must be
#defined, but not both."
#endif
如何檢查處理器是big-endian還是little-endian? 由於聯合體union的存放順序是所有成員都從低地址開始存放,利用該特性就可以輕鬆地獲得了CPU對記憶體採用Little-endian還是Big-endian模式讀寫。例如: union
{ unsigned int a; unsigned char b; }c;
int checkCPUendian()
{ c.a = 1; return (c.b == 1); } /*return 1 : little-endian, return 0:big-endian*/