Chinaunix首页 | 论坛 | 博客
  • 博客访问: 432258
  • 博文数量: 125
  • 博客积分: 2066
  • 博客等级: 大尉
  • 技术积分: 1032
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-16 14:06
文章分类

全部博文(125)

文章存档

2011年(125)

分类: C/C++

2011-04-24 14:03:33

 

 【大端(Big_Endian)与小端(Little_Endian)简介】.doc   

 

【大端(Big Endian)与小端(Little Endian)简介】

Endianness 的问题实质就是关于计算机如何存储大的数值的问题。

我们知道一个基本存储单元可以保存一个字节,每个存储单元对应一个地址。对于大于十进制25516进制0xff)的整数,需要多个存储单元。例如,4660对应于0x1234,需要两个字节。不同的计算机系统使用不同的方法保存这两个字节。在我们常用的PC机中,低位的字节0x34保存在低地址的存储单元,高位的字节0x12保存在高地址的存储单元;而在Sun工作站中,情况恰恰相反,0x34位于高地址的存储单元,0x12位于低地址的存储单元。前一种就被称为Little Endian,后一种就是Big Endian

 

如何记住这两种存储模式?其实很简单。首先记住我们所说的存储单元的地址总是由低到高排列。对于多字节的数值,如果先见到的是低位的字节,则系统就是Little Endian的,Little 就是"小,少"的意思,也就对应""。相反就是Big Endian,这里 Big ""对应""

为了加深对Endianness的理解,让我们来看下面的C程序例子:

 char a = 1; 

 

 

 

 char b = 2;

 

        地址偏移量

 内存映像

 short c = 255;

/* 0x00ff */

         0x0000:

 01 02 FF 00

 long d = 0x44332211;

 

         0x0004:

 11 22 33 44

在右侧我们可以见到在基于Intel 80x86的系统上的内存映像,显然我们可以马上判定这一系统是Little Endian的。对于16位的整形数(short)c,我们先见到其低位的0xff,下一个才是0x00。同样对于32位长整形数(long)d,在最低的地址0x0004存的是最低位字节0x11。如果是在Big Endian的计算机中,则地址偏移量从0x00000x0007的整个内存映像将为:01 02 00 FF 44 33 22 11

     用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

 

 


Big Endian

  
低地址                                            高地址
   ----------------------------------------->
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     12     |      34    |     56      |     78    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

  
低地址                                            高地址
   ----------------------------------------->
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     78     |      56    |     34      |     12    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

所有计算机处理器都必须在这两种Endian间作出选择。但某些处理器(MIPSIA-64)支持两种模式,可由编程者通过软件或硬件设置一种Endian。以下是一个处理器类型与对应的Endian的简表:

·  Big Endian: Sun ,

·  Bi-Endian, 运行Big Endian模式: 运行IRIX, ,大多数系统

·  Bi-Endian, 运行Little Endian模式:   运行Ultrix,大多数DEC , 运行Linux

·  Little Endian: DEC

如何在程序中检测本系统的Endianess?可调用下面的函数来快速验证,如果返回值为1,则为Little Endian;为0则是Big Endian

int testendian() {
    int x = 1;
    return *((char *)&x);
}

Endianness对于网络通信也很重要。试想当Little Endian系统与Big Endian的系统通信时,如果不做适当处理,接收方与发送方对数据的解释将完全不一样。比如对以上C程序段中的变量dLittle Endian发送方发出11 22 33 44四个字节,Big Endian接收方将其转换为数值0x11223344。这与原始的数值大相径庭。为了解决这个问题,TCP/IP协议规定了专门的"网络字节次序",即无论计算机系统支持何种Endian,在传输数据时,总是数值最高位的字节最先发送。从定义可以看出,网络字节次序其实是对应Big Endian的。

为了避免因为Endianness造成的通信问题,及便于软件开发者编写易于平台移植的程序,特别定义了一些C语言预处理的宏来实现网络字节与主机字节次序之间的相互转换。htons()htonl()用来将主机字节次序转成网络字节次序,前者应用于16位无符号数,后者应用于32位无符号数。ntohs()ntohl()实现反方向的转换。这四个宏的原型定义可参考如下(Linux系统中可在netinet/in.h文件里找到)

#if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)

#define htons(A)  (A)

#define htonl(A)  (A)

#define ntohs(A)  (A)

#define ntohl(A)  (A)

#elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)

#define htons(A)  ((((uint16)(A) & 0xff00) >> 8) | (((uint16)(A) & 0x00ff) << 8))

#define htonl(A)  ((((uint32)(A) & 0xff000000) >> 24) | (((uint32)(A) & 0x00ff0000) >> 8)  | (((uint32)(A) & 0x0000ff00) << 8)  | (((uint32)(A) & 0x000000ff) << 24))

#define ntohs     htons

#define ntohl     htohl

#else

#error "Either BIG_ENDIAN or LITTLE_ENDIAN must be #defined, but not both."

#endif

 

 

Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering,或Byte Order 对于数据中跨越多个字节的对象, 我们必须为它建立这样的约定:
    (1)
它的地址是多少?
    (2)
它的字节在内存中是如何组织的?
     
针对第一个问题,有这样的解释:
     
对于跨越多个字节的对象,一般它所占的字节都是连续的,它的地址等于它所占字节最低地址。(链表可能是个例外, 但链表的地址可看作链表头的地址)
     
比如: int x 它的地址为0x100 那么它占据了内存中的Ox100 0x101 0x102 0x103这四个字节(32位系统,所以int占用4个字节)。
     
上面只是内存字节组织的一种情况: 多字节对象在内存中的组织有一般有两种约定。 考虑一个W位的整数。
     
它的各位表达如下:[Xw-1 Xw-2 ... X1 X0],它的
     MSB (Most Significant Byte
最高有效字节) [Xw-1 Xw-2 ... Xw-8];
     LSB (Least Significant Byte
最低有效字节) [X7X6... X0]
     
其余的字节位于MSB LSB之间。
LSB
MSB谁位于内存的最低地址, 即谁代表该对象的地址?
这就引出了大端(Big Endian)与小端(Little Endian)的问题。
如果LSBMSB前面, LSB是低地址, 则该机器是小端; 反之则是大端。
大端和小端是针对字节为单位定义的。
DEC (Digital Equipment Corporation
,现在是Compaq公司的一部分)Intel的机器(X86平台)一般采用小端。
IBM
Motorola(Power PC) Sun的机器一般采用大端。
当然,这不代表所有情况。有的CPU即能工作于小端, 又能工作于大端, 比如ARM Alpha,摩托罗拉的PowerPC 具体情形参考处理器手册。
具体这类CPU是大端还是小端,应该和具体设置有关。
(如,Power PC支持little-endian字节序,但在默认配置时是big-endian字节序)
一般来说,大部分用户的操作系统(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,Big Endian 的。
所以说,Little Endian还是Big Endian与操作系统和芯片类型都有关系。
Linux
系统中,你可以在/usr/include/中(包括子目录)查找字符串BYTE_ORDER(
_BYTE_ORDER, __BYTE_ORDER)
,确定其值。BYTE_ORDER中文称为字节序。这个值一般在endian.hmachine/endian.h文件中可以找到,有时在feature.h中,不同的操作系统可能有所不同。
对于一个数0x1122
使用Little Endian方式时,低字节存储0x22,高字节存储0x11
而使用Big Endian方式时,  低字节存储0x11, 高字节存储0x22
【用函数判断系统是Big Endian还是Little Endian
bool IsBig_Endian()
//
如果字节序为big-endian,返回true;
//
反之为    little-endian,返回false
{
     unsigned short test = 0x1122;
     if(*( (unsigned char*) &test ) == 0x11)
        return TRUE;
else
     return FALSE;
}//IsBig_Endian()

 

     其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。


 

 

 

阅读(1698) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~