计算机数据存储有两种字节优先顺序:高位字节优先(称为大端模式)和低位字节优先(称为小端模式)。
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
例子:对于内存中存放的数0x12345678来说
如果是采用大端模式存放的,则其真实的数是:0x12345678
如果是采用小端模式存放的,则其真实的数是:0x78563412
如果称某个系统所采用的字节序为主机字节序,则它可能是小端模式的,也可能是大端模式的。
而端口号和IP地址都是以网络字节序存储的,不是主机字节序,网络字节序都是大端模式。
要把主机字节序和网络字节序相互对应起来,需要对这两个字节存储优先顺序进行相互转化。
这里用到四个函数:htons(),ntohs(),htonl()和ntohl().
这四个地址分别实现网络字节序和主机字节序的转化,这里的h代表host,n代表network,s代表short,l代表long。
通常16位的IP端口号用s代表,而IP地址用l来代表。
#include <arpa/inet.h> 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 表示 host to network long ,用于将主机 unsigned int 型数据转换成网络字节顺序;
htons 表示 host to network short ,用于将主机 unsigned short 型数据转换成网络字节顺序;
ntohl、ntohs 的功能分别与 htonl、htons 相反。
如图,i为int类型占4个字节,但只有1个字节的值为1,另外3个字节值为0;取出低地址上的值,当其为1时则为小端模式,为0时为大端模式。
//大小端模式的判断 //方法一:利用联合体所有成员的起始位置一致, //对联合体中的int类型赋值,然后判断联合体中char类型的值的大小
#include <iostream> #include <iomanip> using namespace std; //signed typedef signed char int8; typedef short int16; typedef int int32; typedef long long int64; //unsigned typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef unsigned long long uint64; #pragma pack(push) #pragma pack(1)//单字节对齐 typedef struct{ uint32 ID; uint32 Num; uint32 Type; uint32 lat; uint32 lng; uint32 alt; uint32 speed; }Waypoint;//Payload_Data #pragma pack(pop) void EndianSwap(uint8 *pData, int startIndex, int length); int main() { Waypoint wp,wp_ori; int len = sizeof(Waypoint); cout << "size of Waypoint: " << len << endl; wp.ID = 0x00000011; wp.Num = 0x00002200; wp.Type = 0xDD0CB0AA; wp.lat = 0x00330000; wp.lng = 0x44000000; wp.alt = 0xABCD1234; wp.speed = 0x12345678; wp_ori = wp; int i = 0; uint8* pData = (uint8*)(&wp); for (i = 0; i < len; i += 4) { EndianSwap(pData,i,4); } cout << endl; cout << uppercase << hex << "改变字节序前: 0x" << setfill('0') << setw(8) << wp_ori.ID << endl; cout << uppercase << hex << "改变字节序后: 0x" <<setfill('0') << setw(8) << wp.ID <<endl; cout << endl; cout << uppercase << hex << "改变字节序前: 0x" << setfill('0') << setw(8) << wp_ori.Num << endl; cout << uppercase << hex << "改变字节序后: 0x" << setfill('0') << setw(8) << wp.Num << endl; cout << endl; cout << uppercase << hex << "改变字节序前: 0x" << setfill('0') << setw(8) << wp_ori.Type << endl; cout << uppercase << hex << "改变字节序后: 0x" << setfill('0') << setw(8) << wp.Type << endl; cout << endl; cout << uppercase << hex << "改变字节序前: 0x" << setfill('0') << setw(8) << wp_ori.lat << endl; cout << uppercase << hex << "改变字节序后: 0x" << setfill('0') << setw(8) << wp.lat << endl; cout << endl; cout << uppercase << hex << "改变字节序前: 0x" << setfill('0') << setw(8) << wp_ori.lng << endl; cout << uppercase << hex << "改变字节序后: 0x" << setfill('0') << setw(8) << wp.lng << endl; cout << endl; cout << uppercase << hex << "改变字节序前: 0x" << setfill('0') << setw(8) << wp_ori.alt << endl; cout << uppercase << hex << "改变字节序后: 0x" << setfill('0') << setw(8) << wp.alt << endl; cout << endl; cout << uppercase << hex << "改变字节序前: 0x" << setfill('0') << setw(8) << wp_ori.speed << endl; cout << uppercase << hex << "改变字节序后: 0x" << setfill('0') << setw(8) << wp.speed << endl; return 0; } void EndianSwap(uint8 *pData, int startIndex, int length) { int i,cnt,end,start; cnt = length / 2; start = startIndex; end = startIndex + length - 1; uint8 tmp; for (i = 0; i < cnt; i++) { tmp = pData[start+i]; pData[start+i] = pData[end-i]; pData[end-i] = tmp; } }
运行结果如下: