一、什么是字節序
字節序,顧名思義就是字節的順序。更具體的講,它是多字節數據存儲和傳輸時的字節順序。
二、為什么有“字節序”這個東西?
計算機系統中內存是以字節為單位進行編址的,每個地址單元都唯一的對應着1個字節(8 bit)。
這可以應對char類型數據的存儲要求,因為char類型長度剛好是1個字節,但是有些類型的長度是超過1個字節的(字符串雖然是多字節的,但它本質是由一個個char類型組成的類似數組的結構而已),比如C/C++中,short類型一般是2個字節,int類型一般4個字節等。
因此這里就存在着一個如何安排多個字節數據中各字節存放順序的問題。正是因為不同的安排順序導致了大端存儲模式和小端存儲模式的存在。
三、舉個例子
十六進制數 0x12345678 共占4個字節,分別是0x12、0x34、0x56、0x78,因此在該數字中0x12屬於高位數據,0x78屬於地位數據。
注:
可以把內存看成是一個很大的數組,4G內存則是一個長度為4294967296的數組。
該數組的索引其實就是內存地址,左邊是比較小的地址,右邊則越來越大,直至最大值。
第一種順序:高位字節排放在內存的低地址端,低位字節排放在內存的高地址端,叫大端模式
第二種順序:低位字節排放在內存的低地址端,高位字節排放在內存的高地址端,叫小端模式,記憶法:“小端低低”
兩者,大端模式比較符合人類的閱讀習慣;小端模式更符合計算機的處理方式,因為計算機從低位開始處理。
四、模式特定
為什么截然相反的大小端存儲模式能夠並存至今?在標准化備受推崇的今天,為什么大小端誰都沒有被另外一個所同化?我想這除了歷史的慣性使然,還與它們各自的優缺點有關。
大端模式優點:
符號位在所表示的數據的內存的第一個字節中,便於快速判斷數據的正負和大小
小端模式優點:
1、內存的低地址處存放低字節,所以在強制轉換數據時不需要調整字節的內容;
(注解:比如把int的4字節強制轉換成short的2字節時,就直接把int數據存儲的前兩個字節給short就行,因為其前兩個字節剛好就是最低的兩個字節,符合轉換邏輯)
2、CPU做數值運算時從內存中依順序依次從低位到高位取數據進行運算,直到最后刷新最高位的符號位,這樣的運算方式會更高效
其各自的優點就是對方的缺點,正因為兩者彼此不分伯仲,再加上一些硬件廠商的堅持(見1.3節),因此在多字節存儲順序上始終沒有一個統一的標准
四、大小端的應用場景。
Intel的80×86系列芯片使用小端存儲模式
ARM芯片默認采用小端,但可以切換為大端
MIPS芯片采用大端,但可以在大小端之間切換
在網絡上傳輸的數據普遍采用的都是大端
Java 默認采用大端,可以切換大小端切換
五、如何判斷主機序
方法一:通過將多字節數據強制類型轉換成單字節數據,再通過判斷起始存儲位置是數據高字節還是低字節進行檢測
#include<stdio.h> int main() { int x = 0x12345678; char* p = (char*)&x; if(p[0]==12){ printf("Big\n"); } else{ printf("Little\n"); } return 0; }
方法二:利用聯合體union的存放順序是所有成員都從低地址開始存放這一特性進行檢測
#include<stdio.h> int main() { union uendian { int nNum; char cLowAddressValue; }; uendian u; u.nNum = 0x12345678; if ( u.cLowAddressValue == 0x12 ) { printf("Big"); } else { printf("Little"); } return 0; }
Linux 操作系統中相關的源代碼:
static union { char c[4]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } }; #define ENDIANNESS ((char)endian_test.mylong)
分析:利用了聯合體union 的特征,當多個數據需要共享內存或者多個數據每次只取其一時,可以利用聯合體(union)。
在C Programming Language 一書中對於聯合體是這么描述的:
1)聯合體是一個結構;
2)它的所有成員相對於基地址的偏移量都為0;
3)此結構空間要大到足夠容納最"寬"的成員;
4)其對齊方式要適合其中所有的成員;
所以,對 u.nNum 賦值后,可以從 u.cLowAddressValue 獲取低地址的字節內容
六、Java設置大小端
public class HelloEndian { public static void main(String[] args) { ByteBuffer b = ByteBuffer.wrap(new byte[4]); b.putInt(0x01020304); System.out.println("Default-Endian: " + Arrays.toString(b.array())); b = ByteBuffer.wrap(new byte[4]); b.order(ByteOrder.BIG_ENDIAN); b.putInt(0x01020304); System.out.println("Big-Endian: " + Arrays.toString(b.array())); b = ByteBuffer.wrap(new byte[4]); b.order(ByteOrder.LITTLE_ENDIAN); b.putInt(0x01020304); System.out.println("Little-Endian: " + Arrays.toString(b.array())); } }
七、引用
https://segmentfault.com/a/1190000015566120
https://jocent.me/2017/07/25/big-little-endian.html
http://www.cnblogs.com/ziwuge/archive/2010/12/27/1917765.html