位元組序(大小端)
阿新 • • 發佈:2020-12-30
文章目錄
什麼是位元組序
位元組序,簡單來說,就是位元組的順序,指的超過一個位元組的資料型別在記憶體中儲存的順序(一個位元組顯然不需要順序)。
位元組序在不同的主機平臺上分為大端位元組序、小端位元組序。
小端位元組序
高位位元組資料存放在高地址處,低位資料存放在低地址處。linux和windows平臺一般都採用小端位元組序。
採用union可以打印出記憶體中的位元組順序:
void show_little_endian ()
{
union{
char byte[4];
int num;
}test_num;
test_num.num = 0x12345678;
printf("[0]:%p:0x%X\n"
"[1]:%p:0x%X\n"
"[2]:%p:0x%X\n"
"[3]:%p:0x%X\n",
&test_num.byte[0], test_num.byte[0],
& test_num.byte[1], test_num.byte[1],
&test_num.byte[2], test_num.byte[2],
&test_num.byte[3], test_num.byte[3]);
/*
[0]:0x7ffdeb35bb94:0x78
[1]:0x7ffdeb35bb95:0x56
[2]:0x7ffdeb35bb96:0x34
[3]:0x7ffdeb35bb97:0x12*/
}
如上所示,低位元組0x7ffdeb35bb94存放的是地位0x78,高位元組0x7ffdeb35bb97存放的是高位0x12。
大端位元組序
大端就是將高位位元組放到記憶體的低地址端,低位位元組放到高地址端。一般網路上都使用大端位元組序進行二進位制資料的傳輸。
void show_big_endian()
{
union{
char byte[4];
int num;
}test_num;
test_num.num = htonl(0x12345678);
printf("[0]:%p:0x%X\n"
"[1]:%p:0x%X\n"
"[2]:%p:0x%X\n"
"[3]:%p:0x%X\n",
&test_num.byte[0], test_num.byte[0],
&test_num.byte[1], test_num.byte[1],
&test_num.byte[2], test_num.byte[2],
&test_num.byte[3], test_num.byte[3]);
}
/*
[0]:0x7ffd7f6abbd4:0x12
[1]:0x7ffd7f6abbd5:0x34
[2]:0x7ffd7f6abbd6:0x56
[3]:0x7ffd7f6abbd7:0x78
*/
如上所示,低位元組0x7ffd7f6abbd4存放的是低位0x12,高位元組0x7ffd7f6abbd7存放的是高位0x78。
主機位元組序
指執行程式的主機的位元組序,可以採用下面方式獲取主機的位元組序:
bool isLittlEndian()
{
uint32_t n1 = 1;
uint32_t n2 = htonl(n1);
return n1 != n2; // 如果htonl生效(n2!=n1),則說明主機是小端,n2則是按大端位元組序排序的
}
bool isLittlEndian2()
{
union{
char byte[4];
int num;
}test_num;
test_num.num = 0x01u;
return test_num.byte[0] == 0x01u; // 通過低位元組是地位來判斷
}
為什麼要有大小端
小端位元組序的優勢:計算機電路先處理低位位元組,採用小端位元組序效率比較高。
大端位元組序的優勢:資料的儲存方式和人的閱讀方式一致,更利於直接閱讀。
大小端的轉換
將對應的位元組順序顛倒即可
#define BigLittleSwap16(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
(((uint16_t)(A) & 0x00ff) << 8))
#define BigLittleSwap32(A) ((((uint32_t)(A) & 0xff000000) >> 24) | \
(((uint32_t)(A) & 0x00ff0000) >> 8) | \
(((uint32_t)(A) & 0x0000ff00) << 8) | \
(((uint32_t)(A) & 0x000000ff) << 24))
#define BigLittleSwap64(A) ((uint64_t)BigLittleSwap32((A & 0xFFFFFFFF)) << 32 | \
(uint64_t)BigLittleSwap32((A >> 32)))
double、float這類浮點數也需要進行位元組序轉換,轉換方法參考同大小的int
常見型別的主機位元組序和網路位元組序轉換
Linux 和 windows 都提供了介面對資料進行轉換
uint32_t htonl(uint32_t hostlong); // 32位資料(int,int32_t,unsigned int,uint32_t,float)主機序轉網路位元組序
uint16_t htons(uint16_t hostshort); // 16位資料(short,int16_t,unsigned short,uint32_t,float)主機序轉網路位元組序
uint32_t ntohl(uint32_t netlong); // 32位資料(int,int32_t,unsigned int,uint32_t,float)網路位元組序轉主機序
uint16_t ntohs(uint16_t netshort); // 16位資料(int,int32_t,unsigned int,uint32_t,float)網路位元組序轉主機序
int64_t,uint64_t,double可以參考BigLittleSwap64
需要注意的是,windows和linux的long型別定義不同,windows定義的是32位,而linux定義的是64位,網路傳輸時會有歧義,應該避免使用
結構體的大小端轉換
結構體內的每個元素都是獨立的,需要對每個元素進行大小端轉換。