自第一台計算機誕生,其最小儲存單元就被永久的定格了——乙個由8個位元(bit)組成的稱為位元組(byte)的單位。計算機的所有記憶體以位元組陣列的方式進行編址。
當乙個邏輯上長於乙個位元組的整形資料放置在記憶體中時(比如16位,32位,和64位的整數),計算機設計者需要考慮這些位元組的儲存順序。一些體系結構的設計者選擇了將位元組的邏輯順序與物理順序一致,即將邏輯上較低的位元組放置在物理上較低的位元組上;另外一些設計者則選擇了將位元組的邏輯順序與物理順序相反,即將邏輯上較低的位元組放置在物理上較高的位元組上。前者被稱為「little endian」,比如intel x86系列;後者則被稱為「big endian」,比如motorola的powerpc以及sun sparc。還有一些平台同時支援兩種方案,由開發者決定使用哪一種。
兩種選擇為底層開發者帶來了一定的困擾。比如,兩個位元組順序不一致的平台之間進行通訊,或者在兩個位元組順序不一致的平台之間移植系統。這都是跨平台的例子,對於這些情況,位元組順序的問題是不能迴避的。對於僅僅在一種平台上進行開發的程式設計師而言,如果它能夠避免強制型別轉換(比如將位元組陣列強制轉換為乙個長整數),一貫的以邏輯順序來操作大於乙個位元組的整數,應該可以迴避這個問題。但由於c語言是一種非常靈活的語言,有時候通過強制型別轉換可以讓**非常精簡,甚至達到非常巧妙的效果,所以,要求c程式設計師完全迴避這個問題,幾乎是不現實的。
由於little endian提供了邏輯順序與物理順序的一致性,讓程式設計者擺脫了不一致性所帶來的困擾,c語言開發者可以無所顧忌的按照自己的意願進行強制型別轉換,所以現代體系結構幾乎都支援little endian。但big endian也有其優點,尤其對於彙編程式設計師:他們對於任意長度的整數,總是可以通過判斷byte 0的bit-7來檢視乙個整數的正負;對於little endian則不得不首先知道當前整數的長度,然後檢視最高byte的bit-7來判斷其正負。對於這種情況,big endian的開發者可以寫出非常高效的**。
兩派的支持者爭論不休,正像他們所支援名詞(big endian和little endian)的典故所講述的那樣:little endian和big endian這兩個名詞**於jonathan swift的《格利佛遊記》其中交戰的兩個派別無法就應該從哪一端--小端還是大端--開啟乙個半熟的雞蛋達成一致。:)在那個時代,swift是在諷刺英國和法國之間的持續衝突,danny cohen,一位網路協議的早期開創者,第一次使用這兩個術語來指代位元組順序,後來這個術語被廣泛接納了(摘自《深入理解計算機系統》)。
需要特別指出的是,通常所提到的little endian和big endian僅僅指位元組順序。在硬體設計者的術語中,對於乙個位元組內部的bit順序也分little endian和big endian,但對於程式設計師而言,這些bit順序的不同是透明的,也就是說,程式設計師只需要按照邏輯順序來看待和操作位元組內部的bit即可。
endian的不同不僅僅帶來位元組順序的不同,還有更多的問題。如果c程式設計師在定義乙個結構體時,使用了bitwise的域定義,比如:
struct foo ;
這個結構體的乙個物件會占用4個位元組。由於a,b,c,d的型別都是int,所以他們都在以int32為單位的整數上分配bit,另外,由於他們的bit數量正好等於int32的bit數,所以,它們都分配於乙個int所占用的空間。關鍵問題在於這些位元組在這4個位元組內是分配順序是怎麼樣的?
對於little endian,其分配順序與邏輯順序是一致的,即在byte[0]的bit[0~2]上分配a,在byte[0]的bit[3,7]以及byte[1]的bit[0,1]上分配b,依次類推。
對於big endian,其方案會帶來很大的問題。其分配順序為:
位元組物理順序:從低到高;
位元組內bit順序:從高到底;
也就是說,big endian在bitwise的分配方案上,從位元組順序到bit順序都反過來了(因為其正向儲存順序為:位元組從高到底,bit從低到高(從程式設計師的觀點看))。換句話說:big endian的bit分配順序為,按照bit的邏輯順序,從高到底進行分配。
|--------|--------|--------|--------|
logical byte order | byte 3 | byte 2 | byte 1 | byte 0 |
|--------|--------|--------|--------|
bitwise allocation |-a-|---b---|------c------|----d----|
請注意,並不是硬體平台使用的這種方案,而是c語言編譯器。這是一種荒謬的方案,我想可能是c語言編譯器的早期開發者希望通過編譯器遮蔽掉big endian和little endian在bitwise allocation上的差異,而都與物理儲存順序一致。但由於其採用了bit order的反向分配,反而加劇了這種差異,隨後的編譯器為了保持相容,也只好將錯誤延續了下來。
基於這種原因,在c語言中直接使用bitwise的方式定義結構體是一種危險的方式,因為這些**是平台依賴的。當進行跨平台移植的時候必須重新定義這些結構體。
有兩種方式可以消除這種風險:
1、使用邏輯移位的方式來操作bit;以上面的例子為例,我們可以這麼做:
struct foo ;
#define set_a(f,a) do while(0)
#define set_b(f,b) do while(0)
#define set_c(f,c) do while(0)
#define set_d(f,d) do while(0)
#define get_a(f) ((f)&0x7)
#define get_b(f) (((f)>>3)&0x7f)
#define get_c(f) (((f)>>10)&0x1fff)
#define get_d(f) (((f)>>23)&0x1ff)
2、對於big endian,我們可以使用相反的順序來宣告bitwise fields。仍然以上例為例:
#if little_endian
#define bitwise(type,a,b,c,d) type a, b, c, d
#else
#define bitwise(type,a,b,c,d) type d, c, b, a
#endif
struct foo ;
對於little endian,邏輯順序與物理順序一致,只需要按照原樣定義;而對於big endian,由於其整體的bit順序恰好與邏輯順序是相反的,所以,我們將順序反過來,使其bit的分配順序與邏輯順序一致即可。
高位優先與低位優先
endians是什麼意思?它是資料在記憶體中的排列順序。在微處理器中,象long dword 32 bits 0x12345678 這樣的資料總是按照高位優先 big endian 方式存放的。但在記憶體中,資料存放順序則因微處理器廠商的不同而不同。資料大小的不同 byte 乙個位元組,標記為byt...
高位優先與低位優先
endians是什麼意思?它是資料在記憶體中的排列順序。在微處理器中,象long dword 32 bits 0x12345678 這樣的資料總是按照高位優先 big endian 方式存放的。但在記憶體中,資料存放順序則因微處理器廠商的不同而不同。資料大小的不同 byte 乙個位元組,標記為byt...
演算法 位元組高低位交換
對乙個位元組資料,逐個交換其高低位,例如11010001,經過0 7,1 6,2 5,3 4對應位的交換,變成10001011 對於該問題,我們最先想到的是對原位元組通過移位操作來逐位處理,使用另乙個變數來儲存交換後的結果。這種解決方案處理起來思路清晰,編寫 應該不難。下面是該思路對應的 unsig...