有关字节顺序

来源:互联网 发布:淘宝自定义导航代码 编辑:程序博客网 时间:2024/05/29 11:42

在看《linux 网络编程技术》时,提到了字节顺序的问题。知道网络上的机器在表示数据的字节顺序是不同的,如i386芯片是地字节在内存地址的地端,高字节在高端,而alpha芯片却相反。基于intel芯片的计算机采用的是数据存放顺序是倒序存放,称之为主机字节顺序;而internet上传送数据的是顺序存放的,称之为网络字节顺序。在数据被发送到网络之前,必须进行字节顺寻转换。可是还是有许多不明白,在网上找了些资料。

 主机字节顺序(Host)
  Little-Endian [ Intel、VAX和Unisys处理器 等]
 小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处
  网络字节顺序(Network)
  Big-Endian [ IBM 370、Motorola和大多数RISC设计 ---- IBM 大型机和大多数Unix平台 ]

现行的计算机都是以八位一个字节为存储单位,那么一个16位的整数,也就是C语言中的short,在内存中可能有两种存储顺序big- endian和litte-endian.考虑一个short整数0x3132(0x32是低位,0x31是高位),把它赋值给一个short变量,那么它在内存中的存储可能有如下两种情况:

大端字节(Big-endian):
----------------->>>>>>>>内存地址增大方向
short变量地址
        0x1000                   0x1001
_____________________________
|                           |
|         0x31              |        0x32
|________________ | ________________
高位字节在低位字节的前面,也就是高位在内存地址低的一端.可以这样记住(大端->高位->在前->正常的逻辑顺序)
小端字节(little-endian):
----------------->>>>>>>>内存地址增大方向
short变量地址
        0x1000                   0x1001
_____________________________
|                           |
|         0x32              |        0x31
|________________ | ________________
低位字节在高位字节的前面,也就是低位在内存地址低的一端.可以这样记住(小端->低位->在前->与正常逻辑顺序相反)
可以做个实验
在windows上下如下程序
#include <stdio.h>
#include <assert.h>
void main( void )
{
         short test;
         FILE* fp;
         
         test = 0x3132;   //(31ASIIC码的’1’,32ASIIC码的’2’)
         if ((fp = fopen ("c:/test.txt", "wb")) == NULL)
               assert(0);
         fwrite(&test, sizeof(short), 1, fp);
         fclose(fp);
}
     然后在C盘下打开test.txt文件,可以看见内容是21,而test等于0x3132,可以明显的看出来x86的字节顺序是低位在前.如果我们把这段同样的代码放到(big-endian)的机器上执行,那么打出来的文件就是12.这在本机中使用是没有问题的.但当你把这个文件从一个big- endian机器复制到一个little-endian机器上时就出现问题了.
     如上述例子,我们在big-endian的机器上创建了这个test文件,把其复制到little-endian的机器上再用fread读到一个 short里面,我们得到的就不再是0x3132而是0x3231了,这样读到的数据就是错误的,所以在两个字节顺序不一样的机器上传输数据时需要特别小心字节顺序,理解了字节顺序在可以帮助我们写出移植行更高的代码.
正因为有字节顺序的差别,所以在网络传输的时候定义了所有字节顺序相关的数据都使用big-endian,BSD的代码中定义了四个宏来处理:
#define ntohs(n)      //网络字节顺序到主机字节顺序 n代表net, h代表host, s代表short
#define htons(n)      //主机字节顺序到网络字节顺序 n代表net, h代表host, s代表short
#define ntohl(n)       //网络字节顺序到主机字节顺序 n代表net, h代表host, s代表 long
#define htonl(n)       //主机字节顺序到网络字节顺序 n代表net, h代表host, s代表 long
举例说明下这其中一个宏的实现:
#define sw16(x)
     ((short)(
         (((short)(x) & (short)0x00ffU) << 8) |
         (((short)(x) & (short)0xff00U) >> 8) ))
这里实现的是一个交换两个字节顺序.其他几个宏类似.

我们改写一下上面的程序
#include <stdio.h>
#include <assert.h>

#define sw16(x)
     ((short)(
         (((short)(x) & (short)0x00ffU) << 8) |
         (((short)(x) & (short)0xff00U) >> 8) ))

// 因为x86下面是低位在前,需要交换一下变成网络字节顺序
#define htons(x) sw16(x)
void main( void )
{
         short test;
         FILE* fp;
        
         test = htons(0x3132); //(31ASIIC码的’1’,32ASIIC码的’2’)
        if ((fp = fopen ("c:/test.txt", "wb")) == NULL)
               assert(0);
         fwrite(&test, sizeof(short), 1, fp);
         fclose(fp);
}
     如果在高字节在前的机器上,由于与网络字节顺序一致,所以我们什么都不干就可以了,只需要把#define htons(x) sw16(x)宏替换为 #define htons(x) (x).

 

本文出自 “国产0与1” 博客,http://qq164587043.blog.51cto.com/261469/50380