2.8 字节排序

我的环境是在普通 PC(CPU 为 Celeron)上安装了 FreeBSD3.2。sizeof(int)为 4,但在这 4 个字节中,整数究竟是以什么样的形式存放的呢?

这里依然使用一个程序来验证问题(代码清单 2-9)。

代码清单 2-9 byteorder.c

  1. 1: #include <stdio.h>
  2. 2:
  3. 3: int main(void)
  4. 4: {
  5. 5: int hoge = 0x12345678;
  6. 6: unsigned char *hoge_p = (unsigned char *)&hoge;
  7. 7:
  8. 8: printf("%x\n", hoge_p[0]);
  9. 9: printf("%x\n", hoge_p[1]);
  10. 10: printf("%x\n", hoge_p[2]);
  11. 11: printf("%x\n", hoge_p[3]);
  12. 12:
  13. 13: return 0;
  14. 14: }

程序中将 int 型变量强制赋给 unsigned char *型变量 hoge_p,因此,我们可以使用 hoge_p[0]~hoge_p[3] 以字节为单位引用 hoge 的内容。

我的环境中,程序的执行结果如下:

  1. 78
  2. 56
  3. 34
  4. 12

对于我的环境,“0x12345678”在内存中好像是逆向存放的呢。

可能有读者会感到意外。其实 Intel 的 CPU(包括 AMD 等兼容 CPU),都是像这样将整数颠倒过来存放的。这种配置方式一般称为小端(little-endian)字节序。

此外,对于工作站等的 CPU,经常将“0x12345678”这样的值以“12,34,56,78”的顺序存放,这种配置方式称为大端(big-endian)字节序。

那么,小端和大端这样的字节排列方式就称为字节排序(Byte Order)。

小端和大端哪一种方式会更好?这个问题如同辩论宗教取向一样令人纠结,在这里我们就不做深入讨论了。事实是它们各有所长。在纸和笔的时代,人类在做加法运算时都是从低位开始的,可是对于 CPU 来说采用小端方式会更轻松。当然,对于人类来说,大端的方式也许更容易理解。

问题就在于,连以上这样整数类型的数据在内存中的配置方式都因 CPU 的不同而不同

事实上,有的 CPU 还会采用“将两个字节编成一组再反向排列”这样差异性更大的字节排列方式。此外,很多环境使用 IEEE 754 规定的形式处理浮点类型,但 C 的标准并没有规定这一点*。即使是采用 IEEE 754 的处理环境,Intel 的 CPU 也还是逆向排列字节的。

* Java 也规定了这一点。因此,如果硬件不支持 IEEE 754,情况就会变得复杂一点。

也就是说,根据环境的不同,内存中的二进制映像的形式也不尽相同,所以那些试图将内存的内容直接输出到硬盘,或者通过网络进行传输以便不同的机器读取等想法都是不可取的

如果要考虑数据兼容性,建议自定义一些数据格式,然后遵循这些格式来输出数据。UNIX 的 XDR 等工具可以在这一点上为我们提供帮助。

要 点

无论是整数还是浮点小数,内存上的表现形式都随环境的不同而不同。