不同體系的CPU在內存中的數據存儲往往存在着差異。例如,Intel的x86系列處理器將低序字節存儲在起始地址,而一些RISC架構的處理器,如IBM的370主機使用的PowerPC或Motorola公司生產的CPU,都將高序字節存儲在起始位置。這兩種不同的存儲方式被稱為little-endian和big-endian。
little-endian是x86系列CPU的數據存儲方式,即將低序的部分存儲在前面。而big-endian是將高序部分存儲在前面。例如,要存儲0xF432,little-endian將以32F4存儲,而使用big-endian與此相反,將存儲為F432,如圖13.2所示。
程序p13.1.c講解了如何判斷系統是使用big-endian還是little-endian實現數據存儲的。程序中使用的方法如下所示。
圖13.2
big-endian與little-endian方式數據存儲示例
(1)利用聯合的特點。聯合中的數據成員是共享存儲空間的,所分配的空間為數據成員中最大所需的內存數。程序定義了名為endian_un的聯合體,其中包含兩個數據成員,一個是short類型的數據成員(在32位系統上,short類型的長度是2字節),一個是字符類型的字符數組,字符數組的元素個數為short類型的字節數。
程序將var賦值為0x0102。由於聯合結構的特點,bits字符串數組中同樣存儲了0x0102這一數值。通過判斷字符串中的低位和高位存儲的內容,就可以知道系統是little-endian還是big-endian的。
#include
int main(int argc, char **argv) { union { short s; char c[sizeof(short)]; } un;
un.s = 0x0102; if (sizeof(short) == 2) { if (un.c[0] == 1 && un.c[1] == 2) printf("big-endian\n"); else if (un.c[0] == 2 && un.c[1] == 1) printf("little-endian\n"); else printf("unknown\n"); } else printf("sizeof(short) = %d\n", sizeof(short));
exit(0); }
(2)通過強制類型轉換實現。程序中通過取flag變量的地址,獲得起始空間的存儲內容。如果起始空間存儲的是數據的低位內容,則表示存儲方式為little-endian,否則為big-endian。
程序的具體代碼如下:
//使用類型的強制轉換實現little-endian與big-endian的判斷
int is_little_endian(void)
{
unsigned short flag=0x4321;
if (*(unsigned char*)&flag==0x21)
return 1;
else
return 0;
}
使用gcc編譯p13.1.c,獲得名為p13.1的可執行文件。執行該程序,具體輸出如下。可以看到x86系統的內存數據存儲方式為little-endian方式。
[program@localhost charter13]$ gcc -o p13.1 p13.1.c
[program@localhost charter13]$ ./p13.1
judged by first method, little-endian
judged by second method, little-endian
[program@localhost charter13]$
之所以介紹big-endian和little-endian,是因為這一數據存儲方式不僅影響程序在不同硬件平台中的移植,而且在網絡編程中也要考慮字節順序的問題。為了避免兼容性的問題,網絡中的數據傳輸都使用了從高到低的順序存儲方式。因此,如果要將數據從低位字節優先(little-endian)的機器上發往網絡,必須首先進行轉換。而big-endian的機器是不需要轉換的。
Linux系統提供了htons、htonl、ntohs、ntoh這4個函數用於進行字節順序的轉換。其中,h是host的縮寫,n表示network。最后一個字符如果是s,表示short類型,如果是l,表示為long類型。4個函數的具體定義如下:
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
htonl/htons:表示主機字節順序轉換成網絡字節順序,htonl函數和htons函數的區別在於參數長度存在差異。
ntohl/ntohs:表示網絡字節順序轉換成主機字節順序,ntohl函數和ntohs函數的區別在於參數長度存在差異。
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
htonl/htons:表示主機字節順序轉換成網絡字節順序,htonl函數和htons函數的區別在於參數長度存在差異。
ntohl/ntohs:表示網絡字節順序轉換成主機字節順序,ntohl函數和ntohs函數的區別在於參數長度存在差異。