我们有时候经常被大端,小端,网络字节序搞得很迷糊,本文理清一些概念,并给出可移植的代码。
我们的主机字节序,即我们的机器存放内存里的顺序,有两种,一种是大端,另一种是小端,大部分的系统都是小端。
在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节,比如一个unsigned short的变量的值为十六进制"0x0a 0x0b"对应的十进制2571,这个变量跨了1个字节,占了两个字节,内存的地址都是从低位到高位的,那么0x0b是字数据的低字节,应该存储在低地址,0x0a应该存储在高地址,所以这个变量对应的内存字节buff应该是"0x0b 0x0a"。大端正好相反,低地址中存放的是字数据的高字节。
既然不同的机器可能字节序不同,那么网络中发送的数据会不会乱序呢?这里就引出了网络字节序,网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian大端排序方式。
我们可以想到,如果一个unsinged short的变量值代表一个东东的长度,是十六进制"0x0a 0x0b"对应的十进制2571,那么它传送到网络上采用大端模式,对应的字节buff也是"0x0a 0x0b",和我们的主观顺序一致。而接收端如果是大端,则直接将该buff将转为unsigned short型就可以了,如果是小端,强转后肯定乱了序。下面贴上可移植代码。
#include <byteswap.h> #include <iostream> using namespace std; int main() { char net_byte_order[2]; //假设net_byte_order是网络传过来的字节序,对应的十进制是2571(0x0A 0x0B) net_byte_order[0]=0x0A; net_byte_order[1]=0x0B; //强转成unsgined short的值 unsigned short output = *((unsigned short*)net_byte_order); #if BYTE_ORDER == LITTLE_ENDIAN printf("该系统是小端系统,强转后的十六进制是%02x,十进制是%d,需要交换\n",output,output); output = __bswap_16(output); #endif printf("最后值为%02x,十进制是%d\n", output,output); return 0; }
代码中用了判断BYTE_ORDER==LITTLE_ENDIAN,问系统主机字节是否是小端。调用了__bswap_16的宏,该宏在byteswap.h里有定义,当然如果是int型,还有__bswap_32的宏。我们可以观察输出如下:
下面举另一个例子帮助理解大端和小端,假如你想让一个值为2571(0x0a 0x0b),怎样用字符的赋值?上代码:
#include <iostream> using namespace std; int main() { unsigned short output; #if BYTE_ORDER == LITTLE_ENDIAN ((unsigned char*)&output)[0] = 0x0b; ((unsigned char*)&output)[1] = 0x0a; #else ((unsigned char*)&output)[0] = 0x0a; ((unsigned char*)&output)[1] = 0x0b; #endif printf("最后值为%x,十进制是%d\n", output,output); return 0; }
这里我们可以看到,如果是小端的话,0x0b是数据的低有效位,应该放入output内存的低地址((unsigned char*)&output)[0]中。如果是大端,则把0x0b放入高地址。最后输出结果都一样,如下: