字节序问题

来源:互联网 发布:支付宝登录淘宝被限制 编辑:程序博客网 时间:2024/06/07 06:18

      前几天编程遇到字节序的问题,虽然凭着以前学过的知识印象,解决了字节顺序的问题,但是具体的知识以及概念不是很清楚,所以在网络上搜集了一下相关的资料。在此做一些总结。

     

    在各种计算机体系结构中,对于字节、字等的存储机制有所不同,因而引发了计算机通信领域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送。如果不达成一致的规则,通信双方将无法进行正确的编 / 译码从而导致通信失败。目前在各种体系的计算机中通常采用的字节存储机制主要有两种:big-edian (大端)和 little-endian (小端)。


词源: Jargon File 记载, endian 这个词来源于 Jonathan Swift 1726 年写的讽刺小说 "Gulliver's Travels" (《格利佛游记》)。该小说在描述 Gulliver 畅游小人国时碰到了如下的一个场景。在小人国里的小人因为非常小(身高 6 英寸 )所以总是碰到一些意想不到的问题。有一次因为对水煮蛋该从大的一端( Big-End )剥开还是小的一端( Little-End )剥开的争论而引发了一场战争,并形成了两支截然对立的队伍:支持从 Big-End 剥开的人 Swift 就称作 Big-Endians 而支持从 Little-End 剥开的人就称作 Little-Endians ……(后缀 ian 表明的就是支持某种观点的人 :- )。 Endian 这个词由此而来。

   1980 年, Danny Cohen 在其著名的论文 "On Holy Wars and a Plea for Peace" 中为了平息一场关于在消息中字节该以什么样的顺序进行传送的争论而引用了该词。该文中, Cohen 非常形象贴切地把支持从一个消息序列的 MSB 开始传送的那伙人叫做 Big-Endians ,支持从 LSB 开始传送的相对应地叫做 Little-Endians 。此后 Endian 这个词便随着这篇论文而被广为采用。

   

最高有效位 MSB: Most Significant Bit

     最高有效位 (MSB) ,有时候叫做最左边的位,是在一个 n 位二进制数字中的 n-1 位,这个位有最高的权重 (2^(n-1)) 。第一个或最左边的位,当这个数字被用一般的方式书写时。

 

 

最低有效位 LSB: Least Significant Bit

     最低有效位 (LSB) 是给这些单元值的一个二进制整数位位置,就是,决定是否这个数字是偶数或奇数。 LSB 有时候是指最右边的位,因为写较不重要的数字到右边位置符号的协定。它类似于一个十进制整数的最不重要的数字,它是在一个 ( 最右边 ) 位置的数字。

大端字节序( big-endian

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

  • 数据以8bit为单位 :
地址增长方向   → ... 0x0A 0x0B 0x0C 0x0D ...

示例中,最高有效位 (MSB, Most Significant Byte)是0x0A 存储在最低的内存地址处。下一个字节0x0B存在后面的地址处。正类似于十六进制字节从左到右的阅读顺序。

  • 数据以16bit为单位 :
地址增长方向   → ... 0x0A0B 0x0C0D ...

最高的16bit单元0x0A0B存储在低位。



小端字节序( little-endian

  • 数据以8bit为单位 :
地址增长方向   → ... 0x0D 0x0C 0x0B 0x0A ...

最低有效位 (LSB,Least Significant Byte)是0x0D 存储在最低的内存地址处。后面字节依次存在后面的地址处。

 

  • 数据以16bit为单位 :
地址增长方向   → ... 0x0C0D 0x0A0B ...

最低的16bit单元0x0C0D存储在低位。

  • 更改地址的增长方向 :

当更改地址的增长方向,使之由右至左时,表格更具有可阅读性。

←  地址增长方向 ... 0x0A 0x0B 0x0C 0x0D ...

最低有效位(LSB)是0x0D 存储在最低的内存地址处。后面字节依次存在后面的地址处。

←  地址增长方向 ... 0x0A0B 0x0C0D ...

最低的16bit单元0x0C0D存储在低位。

 

网络字节序

     网络传输一般采用大端序,也被称之为网络字节序 ,或网络序 。IP 协议中定义大端序为网络字节序。

伯克利 socket API定义了一组转换函数,用于16和32bit整数在网络序和本机字节序 之间的转换。htonl,htons用于本机序转换到网络序;ntohl,ntohs用于网络序转换到本机序。

   4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于 TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。比如,以太网头部中2字节的“以太网帧类型”,表示后面数据的类型。对于ARP请求或应答的以太网帧类型 来说,在网络传输时,发送的顺序是 0x08,0x06。在内存中的映象如下图所示:
栈底 (高地址)
---------------
0x06 -- 低位 
0x08 -- 高位
---------------
栈顶 (低地址)
该字段的值为0x0806。按照大端方式存放在内存中。

 

高/低地址与高低字节

     首先我们要知道我们C程序映像中内存的空间布局情况:在《C专家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:
----------------------- 最高内存地址 0xffffffff
 | 栈底
 .
 .              栈
 .
  栈顶
-----------------------
 |
 |
/|/

NULL (空洞)

/|/
 |
 |
-----------------------
                堆
-----------------------
未初始化的数据
----------------(统称数据段)
初始化的数据
-----------------------
正文段(代码段)
----------------------- 最低内存地址 0x00000000

以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢[注1]?看下图:
栈底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
栈顶 (低地址)

现在我们弄清了高低地址,接着来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(呵呵,恰好是把上面的那4个字节buf看成一个整型),那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿 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-Endian。

 

 

     在使用系统提供的字节序转换函数时,一定要清楚地知道要转换的数据的长度,比如说在UNIX/Linux上的(通用x86机)
uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);
函数,但是我们可以看到,系统提供的函数是32bit和16bit 的,但是如果我要转换的是一个字符,或是小于一个字符的几个bit的数据呢?(完全可能,像IP包的首部长度就只有4bit)
答案是不必转换,因为在机器存储和网络发送的一个字符内部的bit位存储顺序是一致的,正如我们在上面讲的,不必去转换。

 

 

补充(2010.09.18):


这段时间又有遇到了这个问题,觉得前面的一些东西并不能完全很好的说明字节序的问题,或者说,这些只是理论知识,有一些东西并没有完全说明白,尤其是对于我这种刚刚接触嵌入式开发的人。所以,今天又查阅了很多资料,终于明白大端处理器、小端处理器以及字节序的问题。在此做一下补充:

(1)大端处理器其实就是先处理高字节的数据,后处理低字节的数据。小端处理器就是先处理低字节的数据,后处理高字节的数据。

    跟上面相结合,其实是无论大端处理器和小端处理器都是先处理低地址的数据,所以就有了大端(big-endian)是将高字节放入低地址,低字节放入高地址。小端(little-endian)同样。

(2)前面所提的高地址、低地址其实是针对大于一个字节的结构(类型)的数据的内存存放位置而言的,可是说这个高低之分是相对于一个结构(或者类型或者变量)内部而言的。跨过这个结构(类型、变量)的范围就没有字节序处理的意义和必要了。

 

 

原创粉丝点击