Big-endian和Little-endian

簡而言之:
Big endian machine: It thinks the first byte it reads is the biggest.
Little endian machine: It thinks the first byte it reads is the littlest.
舉個例子,從內存地址0x0000開始有以下數據
0x0000      0x12
0x0001      0x34
0x0002      0xab
0x0003      0xcd
如果我們去讀取一個地址爲0x0000的四個字節變量,若字節序爲big-endian,則讀出
結果爲0x1234abcd;若字節序位little-endian,則讀出結果爲0xcdab3412.
如果我們將0x1234abcd寫入到以0x0000開始的內存中,則結果爲
                 big-endian      little-endian
0x0000      0x12               0xcd
0x0001      0x23               0xab
0x0002      0xab               0x34
0x0003      0xcd               0x12
x86系列CPU都是little-endian的字節序.

-------------------------------------------------------------

再網上查閱了很多資料(包括中英版的MSDN),反覆體會,才基本弄明白一些Big-Endian和Little-Endian的含義,先總結如下:

Big-Endian 和 Little-Endian 字節排序

字節排序含義
Big-Endian 一個Word中的高位的Byte放在內存中這個Word區域的低地址處。
Little-Endian 一個Word中的低位的Byte放在內存中這個Word區域的低地址處。

必須注意的是:表中一個Word的長度是16位,一個Byte的長度是8位。如果一個數超過一個Word的長度,必須先按Word分成若干部分,然後每一部分(即每個Word內部)按Big-Endian或者Little-Endian的不同操作來處理字節。

一個例子:
如果我們將0x1234abcd寫入到以0x0000開始的內存中,則結果爲
                big-endian     little-endian
0x0000     0x12              0xcd
0x0001     0x34              0xab
0x0002     0xab              0x34
0x0003     0xcd              0x12
(注意:0xab換算成2進制是10101011,是個8位的數。我剛纔居然當成4位了,自己把自己搞暈了,shit。)

-----------
疑問:爲什麼要以一個Word爲基礎單位來分割而不是一個DoubleWord或者Byte?究竟就是這麼定義的還是跟具體的CPU有關,跟具體的模式(比如說16位模式、32位模式、64位模式的LE和BE定義會不同)有關?我實在是不清楚,望知道的大俠不吝指點,也希望有這方面資料(文章、代碼)的朋友貼一些上來。

posted on 2005-02-22 23:59 乾坤一笑 閱讀(21662) 評論(39) 編輯 收藏

------------------------------------------------------------------

什麼是字節序?
   字節序,顧名思義字節的順序,再多說兩句就是大於一個字節類型的數據在內存中的存放順序(一個字節的數據當然就無需談順序的問題了)。其實大部分人在實際的開發中都很少會直接和字節序打交道。唯有在跨平臺以及網絡程序中字節序纔是一個應該被考慮的問題。在所有的介紹字節序的文章中都會提到字節序分爲兩類: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)

#elif 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模式讀寫。例如:
   int checkCPUendian(){
        union {
             unsigned int a;
             unsigned char b;            
        }c;
        c.a = 1;
        return (c.b == 1);       
   }   /*return 1 : little-endian, return 0:big-endian*

 

 

鏈接地址:http://hi.baidu.com/savagert/blog/item/21703635ffc9361590ef3948.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章