Never save something for a special occasion. Every day in your life is a special occasion.
分类:
2010-07-25 15:30:38
字节序
目录 |
关键字:端模式、主机序、网络字节序
整数(short/int/long)各字节在内存中保存的顺序问题,称为Endian,又称 主机序(本地充).
Big-Endian(BE),就是高位字节(权值较大的字节)排放在内存的低地址端,低位字节排放在内存的高地址端。
Little-Endian(LE), 就是低位字节(权值较小的字节)排放在内存的低地址端,高位字节排放在内存的高地址端。
eg
双字 0x01020304(DWORD) 在内存中的分布
地址 4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04
特点:
Big-Endian,类似字符串的存储,直观。
Little-Endian,符合人的一般思维(第一感觉), 低位值小,存放在内存地址小的地方; ...
小端模式——X86系列,很多的ARM、DSP都为小端模式,...
小端模式——PowerPC,KEIL C51
可选端模式——有些ARM处理器还可以由硬件(跳线)来选择是大端模式还是小端模式。
short int x;
char x0,x1;
x=0x1122;
x0=((char*)&x)[0]; //低地址单元
x1=((char*)&x)[1]; //高地址单元
若x0=0x11,则是大端; 若x0=0x22,则是小端
这里也可以看出,数据寻址时,用的是低位字节的地址。
或者
bool IsBig_Endian()
{
unsigned short test = 0x1234;
if(*( (unsigned char*) &test ) == 0x12)
return TRUE;
else
return FALSE;
}//IsBig_Endian()
对于WORD(2字节),高低字节颠倒.
对于DWORD(4字节),先两个WORD间颠倒, 然后两个WORD再各自颠倒。
对于WORD型
#define cvt_end2(s) (((s<<8) & 0xFF00) | ((s>>8) & 0xFF))
对于DWORD型
#define cvt_end4(s) (((s<<24) & 0xFF000000) | ((s<<8) & 0xFF0000) | \
((s>>8) & 0xFF00) | ((s>>24) & 0xFF))
网络字节序是 TCP/IP 中规定的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而保证数据在不同主机之间传输时能够被正确解释。
网络字序采用 big endian 方式。
1、内存块传输
先按字节传输,传输后再进行字节序的转换。
2、将整数转换为网络字节序后再传输
先转换为网络字节序,再传输。
示例:
PCbig 大端的平台
PClittle 小端平台
两个平台上的程序都定义如下数据结构
typedef struct pack_tag
{
unsigned short a;
unsigned short b;
}PACK;
最先声明的成员,分配在最低地址。
关于结构内存分布的细节,结构体一节再讲。
现在要将2个unsigned short数据 0x1234, 0x5678 从 PCbig 传输到 PClittle 上,可以通过上面提到的2种方式进行。
PCbig
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
PACK *pPackSend = (PACK*)pBuffer; // 指向内存
pPackSBuf->a = 0x1234; // 将数据存储在结构中
pPackSBuf->b = 0x5678;
SockSendBuf(pBuffer, sizeof(PACK)); // 数据块发送到PClittle
PClittle
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
SockRecvBuf(pBuffer, sizeof(PACK)); // 从PCbig接收数据块
PACK pack = *((PACK*)pBuffer); // 取得数据块
pack.a = cvt_end(pack.a); // 端模式转换
pack.b = cvt_end(pack.b);
废话
数据包在PCbit的内存分布如下
... [12][34] [56][78] ...
数据块发送到PClittle后,在内存分布仍然是这样,
如果不转换而直接通过结构体体去获得数据,则
pack.a = 0x3412
pack.b = 0x7856
通过上面的端模式转换后,数据的内存分布如下
[12][34] [56][78]
此时通过结构体去解析才能获得正确的数据
pack.a = 0x1234
pack.b = 0x5678
PCbig
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
PACK *pPackSend = (PACK*)pBuffer; // 指向内存
pPackSBuf->a = htons(0x1234); // 将数据按网络字节序存储在结构中
pPackSBuf->b = htons(0x5678);
SockSendBuf(pBuffer, sizeof(PACK)); // 数据块发送到PClittle
PClittle
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
SockRecvBuf(pBuffer, sizeof(PACK)); // 从PCbig接收数据块
PACK pack = *((PACK*)pBuffer); // 取得数据块
pack.a = ntohs(pack.a); // 转换为本机序
pack.b = ntohs(pack.b);
关于网络字节序的更多内容,见 网络编程->网络字节序 一节。