最近挺忙的,也沒時間寫點東西,一直在忙下一個資料片的事情,前幾天在群里見有人問關于大小端的事情,這里說一下。
對于跨平臺的程序或者所用數據牽扯到不同平臺的程序(例如網絡編程),大小端字節序是個值得考慮的事情。本文主要討論一下網絡編程方面的大小端問題。(by peakflys)
先來說一下幾個定義:
a) Little-Endian就是
低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。(邏輯上的低低高高)
b) Big-Endian就是
高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。(像數據流一樣填充)
c) 網絡字節序:
TCP/IP各層協議將字節序定義為Big-Endian,因此TCP/IP協議中使用的字節序通常稱之為網絡字節序。 因為字節序往往和具體CPU架構有關,所以 如果你知道你的程序主要用戶群是什么平臺,為了方便或者效率,你可以除了socket端口等需要在主機字節序和網絡字節序之間轉換外,其余數據的傳遞直接無視。例如 現在很多端游 都是如此。因為現在大多數人使用的計算機都是X86體系結構的CPU+Windows操作系統,這部分用戶基本就是主流玩家,其他平臺的玩家,除非獲得的回報率足夠多,否則沒必要花費太多時間關注。
先來說一下,常見的CPU架構的字節序吧:
Big Endian : PowerPC、IBM、Sun
Little Endian : x86、DEC
ARM的大小端是可選的。
最近隨著移動終端(大多為ARM處理器)和移動互聯網的爆發式發展,以后的游戲平臺就不得不考慮一下大小端問題了。
大小端問題主要涉及的是非單字節非字符串外的其余數據的表示和傳遞,如short型、int型等。判斷主機大小端的方法有很多,常見的是聯合體判斷法,代碼如下:


01.bool isBigEndian()


02.

{

03. union


04.

{

05. int a;

06. char b;

07. }num;

08. num.a = 0x1234;

09. return ( num.b == 0x12 )

14.}
出于效率考慮,我們有理由也完全應該 把大小端的處理放在客戶端,在客戶端socket過來時把服務器主機的大小端通知給客戶端,這樣服務器就不需要改動,直接傳遞數據就行,這時候可以在客戶端代碼中封裝幾個宏,在客戶端在收到數據后,根據那些宏來判斷是否轉換以及得出轉換后的數值。大小端轉換最有效也是最常見的方法就是移位法:

#define __SWP16(A) (( ((uint16)(A) & 0xff00) >> 8) | \

(( (uint16)(A) & 0x00ff) << 8))


#define __SWP32(A) ((( (uint32)(A) & 0xff000000) >> 24) | \

(( (uint32)(A) & 0x00ff0000) >> 8) | \

(( (uint32)(A) & 0x0000ff00) << 8) | \

(( (uint32)(A) & 0x000000ff) << 24))
聊了那么多,可能很多人要問 為什么 主機的字節序不統一呢? 這是因為 各個CPU廠商出于不同的邏輯考量,換句話說 大端和小端有其各自的優勢。我們知道計算機正常的內存增長方式是從低到高(當然棧不是),取數據方式是從基址根據偏移找到他們的位置,從他們的存儲方式可以看出,大端存儲因為第一個字節就是高位,從而很容易知道它是正數還是負數,對于一些數值判斷會很迅速。而小端存儲 第一個字節是它的低位,符號位在最后一個字節,這樣在做數值四則運算時從低位每次取出相應字節運算,最后直到高位,并且最終把符號位刷新,這樣的運算方式會更高效,也更符合我們手算的方式。當然這些都是自己的理解,如有不對,還望指正。
此次評述先到此為止,要去給媳婦兒做飯吃了……