字节那些事儿!

2 篇文章 0 订阅
作者: 水寒  来源: 博客园  发布时间: 2010-09-25 14:27  阅读: 335 次   原文链接
1、 前言

  作为一名 C/C++ 程序员,字节是我们天天都要与之打交道的一个东西。我们和它熟稔到几乎已经忘记了它的存在。可是,它自己是不甘寂寞的,或迟或早地,总会在某些时候探出头来张望,然后给你一个腿儿绊。其实,只要你真正了解了它的底细,你就会畅行无阻。在本文中,我们将首先简要了解一下字节的概念,然后着重了解一下字节序问题和字节对齐问题。

注:笔者已经尽最大努力保证本文信息的正确性,但确实无法提供百分之百的担保。

2、 什么是字节

  我们知道,二进制计算机(也就是我们目前接触到的几乎所有的计算机)的最小数据单位是位( bit )。一位数据只能够表示两种含义(需要说明,尽管我们通常把单个位表示的两种含义选择为相互对立的含义,但这并不是必然的,例如你可以认为 1 代表 5 个人, 0 代表 8 个人),对于绝大多数的计算要求,单个位显然不能满足。因此,我们通常都会使用一连串的位,我们可以称之为位串( bit string ,请爱好质疑的的朋友注意,此术语非我杜撰)。由于种种原因,计算机系统都不会让你使用任意长度的位串,而是使用某个特定长度的位串。一些常见的位串长度形式具有约定好的名称,如,半字节( nibble ,貌似用的不多)代表四个位的组合,字节( byte ,主角出场!)代表 8 个位的组合。再多的还有,字( word )、双字( Double word ,通常简写为 Dword )、四字( Quad word ,经常简写为 Qword )、十字节( Ten byte ,也简写为 Tbyte )。

  在这些里面,字( word )有时表示不同的含义。在 Intel 体系里, word 表示一个 16 位的数值,它是固定大小的。而在另外一些场合, word 表示了 CPU 一次可处理的数据的位数,表示一个符合 CPU 字长( word-length )的数目的位串。事实上我们接触较多的 ARM 体系中, word 就有不同的含义,它表示一个 32 位的数据(与机器字长相同),对于 16 位大小的数据, ARM 使用了另外的一个术语,叫作半字( half-word ),请大家在文档阅读时加以注意。另外, Qword 也是 Intel 体系中的术语,其他的体系中可能并不使用。在本文中,我们按照 Intel 的惯例来使用字或者 word 这一术语。

一个字节中共有 8 个数据位,有时需要用图表逐位表述各个位。习惯上,我们按照下面的图来排列各个位的顺序,即,按照从右到左的顺序,依次为最低位(从第 0 位开始)到最高位(对于字节,则是第 7 位):

                             

  字节是大多数现代计算机的最小存储单元,但这并不代表它是计算机可以最高效地处理的数据单位。一般的来说,计算机可以最高效地处理的数据大小,应该与其字长相同。在目前来讲,桌面平台的处理器字长正处于从 32 位向 64 位过渡的时期,嵌入式设备的基本稳定在 32 位,而在某些专业领域(如高端显卡),处理器字长早已经达到了 64 位乃至更多的 128 位。

3、 字节序问题的由来

  对于字、双字这些多于一个字节的数据,如果把它们放置到内存中的某个位置上,可以看出,我们还可以将之看作是字节的序列。一个字是两个字节,双字则是四个字节。假设有以下数据: 0x12345678 、 0x9abcdef0 。在此处,我使用了我们最习惯的十六进制表示法,并给出了两个双字的值。按照惯例,我把双字的左侧视为高端,而把右侧视为低端。把它们顺序放置在起始地址为 0 的内存中,如下图所示:                        

 

 

 

  由图示可知, 0x9abcdef 的相应地址为 0x04 。现在,问题来了,如果有一个内存操作,要从地址 0x06 处读取一个字,得到的结果是多少呢?答案是:不一定。

  这里的本质问题在于,如何把多字节的对象存储到内存中去呢?即使使用最正常的思维去考虑这个问题,你也会发现有两种方法。第一种方法是,把最低端的字节放到指定的起始位置(即基地址处),然后按照从低到高的字节顺序把其余字节依次放入,如下图 a ;另一种方法非常类似,但是对高端字节和低端字节的处理顺序正好相反,如下图 b (我确信你还可以想出其他的方法,但是除二字节的情况外,必然会打破字节排列顺序的一致性,我视之为反常规思维的产物,此处暂不考虑)。

图 a

 

图 b

  在很久之前,哪一种存储方式更为合理曾经有过争论。到今天,争论的结果已经无关紧要了,紧要的是以下事实:这两种存储方式都被应用到了现实的计算机系统中。上图 a 中的排列方式为 Intel 所采用并大行其道,而图 b 的排列方式则被大多数的其他平台采用(如最近被苹果公司彻底抛弃的 PowerPC ),因此上,我们不能称之为罕见的用法。之所以造成事实上的不经常见到,其原因正如我今天中午所得到的消息: Intel 的 CPU 占整个市场份额的 80% 以上。

  这两种排列方式通常用小端( little endian )和大端( big endian )来称谓。这两个奇怪的名字据说来源于童话《格列佛游记》,其中小人国里的公民为了鸡蛋到底是应该从小的一头打开还是大的一头打开而大起争执。 Intel 的方式对应于“小端”,顺便说一句,大端的方式也有一个大公司的名字作为其代表,即最近开始没落的 Motorola 。如果有谁了解过 TIFF 图像文件格式,就会发现其文件头中用以标识文件数据字节序的标志就是“ II ”和“ MM ”,分别对应于 Intel 和 Motorola 的首字母。值得提醒一下,小端方式的排列与位的排列顺序相一致,看上去似乎更协调一些。

  现在我们可以回答上面的问题了。对于小端字节序,我们取到的字,其值为 0x9abc ,而如果是大端字节序的话,就会取到 0xdef0 。

4、 何时会出现字节序问题

  字节序问题主要出现在数据在不同平台之间进行交换时,交换的途径可能是网络传输,也可能是文件复制。例如,如果你设计了一种可能会应用于不同平台的文件格式,其中存储了某些数据结构,则对于大小大于一个字节的数据就要明确地规定其遵循的字节序,以便各平台上的处理程序可以在使用数据时实现做必要的转换。

  举一个实际的例子。 Java 是一个跨平台的编程语言,其可执行文件(扩展名为 .class ,使用的是一种机器无关的字节码指令集)在理论上可以运行于所有的实现了 Java 运行时的平台(包含有与特定平台相关特性的除外)。编译后的 .class 中一定保存有诸如 Integer 这样类型的数据,这就涉及到了字节序的确定,否则 .class 必然不能被采用了不同字节序的平台同时正确加载并运行。事实上, Java 语言采用的为大端字节序,这个一点都不奇怪,因为当初 SUN 公司自己的 SPARC 架构就是采用的大端字节序。同样的问题和解决问题的方式,也存在于操作系统新贵 android 系统上。

  网络传输则是另一个典型场景。 TCP/IP 所采用的网络传输字节序标准也是大端字节序,这个也不必奇怪,因为 TCP/IP 是从 UNIX 系统发展起来的,而绝大部分的 UNIX 系统在很长的一段时间内都没有运行于 Intel 体系架构上的版本。

  处理字节序问题的手段非常简单,也就是对数据进行必要的转换:将十六进制的数字从两端开始交换,直至移动到数据的中心,交换完成为止。交换的结果就好像物体与镜面之内的成像互换了位置,因此也被称为镜像交换( mirror-image swap )。请参看下图:

                                                             

 

 

5、 如何在程序中判断字节序

  在实际的工作中,有时需要对字节序进行判断,然后予以不同的处理。一般的来说,编译后的程序通常只能运行在特定的平台之上,其所采用的字节序方式在编译时即可确定,在这种情况下,程序源代码中通常是把字节序的判别作为条件编译的判断语句,而不会判断代码放在真正的可执行代码中。

  在这里,需要使用我们的老朋友 —— 宏。以下是一个真实的跨平台工程中代码,清晰起见,我稍做了修改:

     
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值