字节那些事儿

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

/dandycheung/archive/2010/09/13/5881620.aspx

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 )。请参看下图:

相关文档
最新文档