一、大端和小端的問題
對於整型、長整型、無符號整型等數據類型,Big endian 認為第一個字節是最高位字節(按照從低地址到高地址的順序存放數據的高位字節到低位字節);而 Little endian 則相反,它認為第一個字節是最低位字節(按照從低地址到高地址的順序存放據的低位字節到高位字節)。
例如,假設從內存地址 0x0000 開始有以下數據:
0x0000 0x0001 0x0002 0x0003
0x12 0x34 0xab 0xcd
如果我們去讀取一個地址為 0x0000 的四個字節變量,若字節序為big-endian,則讀出結果為0x1234abcd;若字節序為little-endian,則讀出結果為0xcdab3412。
如果我們將0x1234abcd 寫入到以 0x0000 開始的內存中,則Little endian 和 Big endian 模式的存放結果如下:
地址 0x0000 0x0001 0x0002 0x0003
big-endian 0x12 0x34 0xab 0xcd
little-endian 0xcd 0xab 0x34 0x12
一般來說,x86 系列 CPU 都是 little-endian 的字節序,PowerPC 通常是 big-endian,網絡字節順序也是 big-endian還有的CPU 能通過跳線來設置 CPU 工作於 Little endian 還是 Big endian 模式。
對於0x12345678的存儲:
小端模式:(從低字節到高字節)
地位地址 0x78 0x56 0x34 0x12 高位地址
大端模式:(從高字節到低字節)
地位地址 0x12 0x34 0x56 0x78 高位地址
二、大端小端轉換方法
htonl() htons() 從主機字節順序轉換成網絡字節順序
ntohl() ntohs() 從網絡字節順序轉換為主機字節順序
Big-Endian轉換成Little-Endian
#define BigtoLittle16(A) ((((uint16)(A) & 0xff00) >> 8) | (((uint16)(A) & 0x00ff) << 8)) #define BigtoLittle32(A) ((((uint32)(A) & 0xff000000) >> 24) | (((uint32)(A) & 0x00ff0000) >> 8) | \ (((uint32)(A) & 0x0000ff00) << 8) | (((uint32)(A) & 0x000000ff) << 24))
三、大端小端檢測方法
如何檢查處理器是big-endian還是little-endian?
C程序:
int i = 1; char *p = (char *)&i; if(*p == 1) printf("Little Endian"); else printf("Big Endian");
大小端存儲問題,如果小端方式中(i占至少兩個字節的長度)則i所分配的內存最小地址那個字節中就存着1,其他字節是0.大端的話則1在i的最高地址字節處存放,char是一個字節,所以強制將char型量p指向i則p指向的一定是i的最低地址,那么就可以判斷p中的值是不是1來確定是不是小端。
聯合體union的存放順序是所有成員都從低地址開始存放,利用該特性就可以輕松地獲得了CPU對內存采用Little-endian還是Big-endian模式讀寫
/*return 1: little-endian, return 0: big-endian*/ int checkCPUendian() { union { unsigned int a; unsigned char b; }c; c.a = 1; return (c.b == 1); }
實現同樣的功能,來看看Linux 操作系統中相關的源代碼是怎么做的:
static union { char c[4]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } }; #define ENDIANNESS ((char)endian_test.mylong)