您的位置:首页 > 理论基础

计算机存储体系结构之大小端存储格式

2013-07-14 12:35 405 查看
目前在计算机存储体系中通常采用的字节存储机制主要有两种:

big-edian 和 littile-endian。

本文简要描述这两种存储机制的特点与区别。

为了叙述方便,下面先对本文中简要用到的两个疏于进行简单定义

1.MSB是Most Significant Bit/Byte 的首字母缩写,通常译为最重要位或者最重要的字节。它通常用来表明在一个bit序列或者一个字节序列中对整个序列影响最大的位或者字节

2.LSB是Least Significant Bit/Byte 的首字母缩写,通常译为最不重要的位或者最不重要的字节,它通常用来表明在一个bit序列或者一个字节序列中对整个序列影响最小的位或者字节

3.big-endian:计算机体系结构中的一种描述多字节存储顺序的术语,在这种机制中最重要的字节(MSB)存放在最低端的地址上。如下:

双字节数0x1234以big-endian的方式存在起始地址0x00000020中

+--------------------------+ | 0x34 |<-- 0x00000021 

+--------------------------+ | 0x12 |<-- 0x00000020 

在Big-Endian中,对于bit序列中的序号编排方式如下(以双字节数0X8B8A为例)

Big-Endian的bit序列编码方式

bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +------------------------------------------------+

val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 | +-------------------------------------------------+

      ^ 0x8B                                 0x8A ^ 

       MSB        
    LSB 

注一:通常在TCP/IP协议中所说的网络序列(Network Order)就是使用Big-Endian规则。在TCP/IP网络通信中,通信双方把消息按照上面图方式编码,然后按照从MSB(bit0)到LSB(bit15)的顺序在网络上传输。

4.little-endian:计算机体系结构中一种描述多字节存储的术语,在这种机制中最不重要的字节(LSB)存放在最低端的地址上,通常用来描述一个字节中各个比特的排放次序。

双字节数0x1234以little-endian的方式存在起始地址0x00000020中

| 0x12 |<-- 0x00000021 +---------------------------+

 | 0x34 |<-- 0x00000020 +---------------------------+

在Little-Endian中,对于bit序列中的序号编排和Big-Endian刚好相反,其方式如下(以双字节数0x8B8A为例):

Little-Endian的bit序列编码方式

bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +-----------------------------------------+

 val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 | +-----------------------------------------+

      ^ 0x8B                                    0x8A ^ 

        MSB                                      LSB

通常我们说的主机序(Host Order)就是使用Litlle - Endian规则。所以当两台主机之间要通过TCP/IP协议进行通信的时候就需要调用相应的函数进行主机序(Little-Endian)到网络序(Big-Endian)的转换。

什么是字节序?

字节序:顾名思义,就是多个字节类型的数据在内存中存放的顺序,在跨平台已经网络程序中字节序才是一个应该考虑的问题。

字节序的两种分类:a: Little-Endian 就是低位字节排放在内存的低地址端,高字节排放在内存的高地址端。

b.Big-Endian就是高位字节排放在内存的低地址端,低位字节拍排放在内存的高地址端。

c.网络TCP/I各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称为网络字节序。

什么是高/低地址 什么是高/低字节

C程序映像中内存空间的分布情况,在《C专家编程》中或者《UNIX环境高级编程》中有关内存的分布情况大致如下:

--------------最高内存地址 0xffffffff -------------

栈底(高地址)

栈…

栈顶(低地址)

------------------------------------------------------- 

NULL (空洞)

 --------------------------------------------------------

 堆

------------------------------------------------------ 

未初始化的数据

--------------------------------------(统称数据段) 

初始化的数据

------------------------------------------------------- 

正文段(代码段)

 ---------------最低内存地址 0x00000000--------

以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢?看下图:

------栈底(高地址)----- 

buf[3]

 buf[2] 

buf[1] 

buf[0] 

----栈顶(低地址)------

高低字节: 拿0x12345678来说,从高位到低位的字节次序依次是0x12 0x34 0x56 0x78

高/低地址端和高/低字节都弄清了。我们再来回顾一下Big-Endian和Little-Endian的定义,

并用图示说明两种字节序:以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,

我们可以用unsigned char buf[4]来表示value:

Big-Endian: 低地址存放高位,如下图:

----------栈底(高地址)---------------

buf[3] (0x78) --低位

buf[2] (0x56)----

buf[1] (0x34)----

buf[0] (0x12) --高位

-----------栈顶(低地址)--------------

Little-Endian: 低地址存放低位,如下图:

------栈底(高地址)--------- 

buf[3] (0x12) ---高位

buf[2] (0x34)---------- 

buf[1] (0x56)---------- 

buf[0] (0x78) ---低位

------栈顶(低地址)--------

现有的平台上Intel的X86采用的是Little-Endian,而像Sun的SPARC采用的就是Big-Edian。那么在跨平台或网络程序中如何实现字节序的转换?这个通过C语言的移位操作很容易实现,例如: 

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

#define htons(A)  (A)

#define htonl(A)   (A)

#define ntohs(A)  (A)

#define ntohl(A)   (A)

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

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

#define htons(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 #define,but not both,"

#endif

如何检查处理器是big-endian 还是little-endian?

由于联合体union的存放顺序是所有成员都是从低地址开始存放,利用该特性就可以轻松地获得了CPU对内存采用的是LITTLE-ENDIAN模式还是BIG-ENDIAN模式读写

union

{

 unsigned int a ;

 unsigned char b;

}c;

int checkCPUendian()

{

   c.a = 1; // 联合体中a现在起作用,联合体任意时刻只有一个变量起作用,各个成员变量共用一个内存空间 (1存放在低地址)

   return (c.b == 1); //  return (c.b == 1) 联合体中b现在起作用

}   // return 1 : little-endian,  说明1存放在低地址 

  //  return 0 :big-endian;  说明1存放在高地址 

参考文章:http://blog.csdn.net/yasaken/article/details/7243757
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: