字节序

简单点说,就是字节的存储顺序,如果数据都是单字节的,那怎么存储无所谓了,但是对于多字节数据,比如int,double等,就要考虑存储的顺序了。注意字节序是硬件层面的东西,对于软件来说通常是透明的。

  • 字节的高位与低位:举个例子,int a = 0x12345678 ; 那么左边12就是高位字节,右边的78就是低位字节,从左到右,由高到低,(注意,高低乃相对而言,比如56相对于78是高字节,相对于34是低字节)

  • 地址的高端与低端:从上到下,由低到高,地址值小的为低端,地址值大的为高端。

大端序与小端序

  • Big-endian(大端序):数据的高位字节存放在地址的低端 低位字节存放在地址高端

小端模式优点:内存的低地址处存放低字节,所以在强制转换数据时不需要调整字节的内容(注解:比如把int的4字节强制转换成short的2字节时,就直接把int数据存储的前两个字节给short就行,因为其前两个字节刚好就是最低的两个字节,符合转换逻辑);CPU做数值运算时从内存中依顺序依次从低位到高位取数据进行运算,直到最后刷新最高位的符号位,这样的运算方式会更高效。

  • Little-endian(小端序):数据的高位字节存放在地址的高端 低位字节存放在地址低端

大端模式优点:符号位在所表示的数据的内存的第一个字节中,便于快速判断数据的正负和大小

大端序是按照数字的书写顺序进行存储的,而小端序是颠倒书写顺序进行存储的。

网络序

不同的计算机使用的字节序可能不同,即有可能有的使用大端模式有的使用小端模式。那使用不同字节序模式的计算机如何进行通信呢? (目前个人PC大部分都是X86的小端模式)
TCP/IP协议隆重出场,RFC1700规定使用“大端”字节序为网络字节序,其他不使用大端的计算机要注意了,发送数据的时候必须要将自己的主机字节序转换为网络字节序(即“大端”字节序),接收到的数据再转换为自己的主机字节序。这样就与CPU、操作系统无关了,实现了网络通信的标准化。

C/C++中有如下四个常用的转换函数,这四个函数在小端系统中生效,大端系统由于和网络字节序相同,所以无需转换。

1
2
3
4
htons —— 把unsigned short类型从主机序转成网络字节序
ntohs —— 把unsigned short类型从网络字节序转成主机序
htonl —— 把unsigned long类型从主机序转成网络字节序
ntohl —— 把unsigned long类型从网络字节序转成主机序

判断字节序

判断的思路是确定一个多字节的值(下面使用的是4字节的整数),将其写入内存(即赋值给一个变量),然后用指针取其首地址所对应的字节(即低地址的一个字节),判断该字节存放的是高位还是低位,高位说明是Big endian,低位说明是Little endian。

1
2
3
4
5
6
7
8
9
10
unsigned int x = 0x12345678;
char *c = (char*)&x;
if (*c == 0x78)
{
// ("Little endian");
}
else
{
// ("Big endian");
}

预定义宏

  • GCC/Cang 定义了 __BYTE_ORDER__ 宏,用于判断大小端。
1
2
3
4
5
6
7
8
9
10
11
12
#if defined(__ANDROID__) 
#include <sys/endian.h>
#elif defined(__APPLE__)
#include <machine/endian.h>
#endif


#if (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN))
// 小端模式
#elif (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN))
// 大端模式
#endif