字節順序Little Endian / Big Endian

 Endianness 的問題實質就是關於計算機如何存儲大的數值的問題。 我們知道一個基本存儲單元可以保存一個字節,每個存儲單元對應一個地址。對於大於十進制255(16進制0xff)的整數,需要多個存儲單元。例如,4660對應於0x1234,需要兩個字節。不同的計算機系統使用不同的方法保存這兩個字節。在我們常用的PC機中,低位的字節0x34保存在低地址的存儲單元,高位的字節0x12保存在高地址的存儲單元;而在Sun工作站中,情況恰恰相反,0x34位於高地址的存儲單元,0x12位於低地址的存儲單元。前一種就被稱爲Little Endian,後一種就是Big Endian。 如何記住這兩種存儲模式?其實很簡單。首先記住我們所說的存儲單元的地址總是由低到高排列。對於多字節的數值,如果先見到的是低位的字節,則系統就是Little Endian的,Little 就是"小,少"的意思,也就對應"低"。相反就是Big Endian,這裏 Big "大"對應"高"。 爲了加深對Endianness的理解,讓我們來看下面的C程序例子:

 char a = 1; char b = 2; 地址偏移量 內存映像 short c = 255; /* 0x00ff */ 0x0000: 01 02 FF 00 long d = 0x44332211; 0x0004: 11 22 33 44

在右側我們可以見到在基於Intel 80x86的系統上的內存映像,顯然我們可以馬上判定這一系統是Little Endian的。對於16位的整形數(short)c,我們先見到其低位的0xff,下一個纔是0x00。同樣對於32位長整形數(long)d,在最低的地址0x0004存的是最低位字節0x11。如果是在Big Endian的計算機中,則地址偏移量從0x0000到0x0007的整個內存映像將爲:01 02 00 FF 44 33 22 11。 所有計算機處理器都必須在這兩種Endian間作出選擇。但某些處理器(如MIPS和IA-64)支持兩種模式,可由編程者通過軟件或硬件設置一種Endian。以下是一個處理器類型與對應的Endian的簡表:

        純Big Endian: Sun SPARC, Motorola 68000,Java Virtual Machine Bi-Endian, 運行Big Endian模式: MIPS運行IRIX, PA-RISC,大多數Power和PowerPC系統 Bi-Endian, 運行Little Endian模式: MIPS 運行Ultrix,大多數DEC Alpha, IA-64運行Linux Little Endian:

       Intel x86,AMD64,DEC VAX 如何在程序中檢測本系統的Endianess?可調用下面的函數來快速驗證,如果返回值爲1,則爲Little Endian;爲0則是Big Endian:int testendian() { int x = 1; return *((char *)&x);}Endianness對於網絡通信也很重要。試想當Little Endian系統與Big Endian的系統通信時,如果不做適當處理,接收方與發送方對數據的解釋將完全不一樣。比如對以上C程序段中的變量d,Little Endian發送方發出11 22 33 44四個字節,Big Endian接收方將其轉換爲數值0x11223344。這與原始的數值大相徑庭。爲了解決這個問題,TCP/IP協議規定了專門的"網絡字節次序",即無論計算機系統支持何種Endian,在傳輸數據時,總是數值最高位的字節最先發送。從定義可以看出,網絡字節次序其實是對應Big Endian的。

        爲了避免因爲Endianness造成的通信問題,及便於軟件開發者編寫易於平臺移植的程序,特別定義了一些C語言預處理的宏來實現網絡字節與主機字節次序之間的相互轉換。htons()和htonl()用來將主機字節次序轉成網絡字節次序,前者應用於16位無符號數,後者應用於32位無符號數。ntohs()和ntohl()實現反方向的轉換。

這四個宏的原型定義可參考如下(Linux系統中可在netinet/in.h文件裏找到):

#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  

來自:[http://packetmania.blogchina.com/blog/article_124455.604021.html p.302 2005年 01月07日

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