字节序是指多字节类型的数据在内存中的存放顺序,通常可分为小端、大端两种字节序。小端字节序(Little-endian)指低数权字节数据存放在内存低地址处,高数权字节数据存放在内存高地址处的内存分布方式;大端字节序(Big-endian)是高数权字节数据存放在低地址处,低字节数据存放在高地址处的内存分布方式。
在采用大端字节序时,0x12345678在内存中的存储方式如下所示。
内存地址 |
字节值 |
---|---|
+0 |
0x12 |
+1 |
0x34 |
+2 |
0x56 |
+3 |
0x78 |
Motorola 68k、IBM POWER系列CPU采用大端字节序。
在采用小端字节序时,0x12345678在内存中的存储方式如下所示。
内存地址 |
字节值 |
---|---|
+0 |
0x78 |
+1 |
0x56 |
+2 |
0x34 |
+3 |
0x12 |
Intel x86系列CPU采用小端字节序。
为了进行演示,我们可以在QEMU的虚拟化环境中安装MIPS Linux。[1]
接下来在MIPS Linux里编译下述程序:
#include <stdio.h>
int main()
{
int v, i;
v=123;
printf ("%02X %02X %02X %02X\n",
*(char*)&v,
*(((char*)&v)+1),
*(((char*)&v)+2),
*(((char*)&v)+3));
};
然后运行下述指令:
root@debian-mips:~# ./a.out
00 00 00 7B
其中,0x7B就是十进制的123。在采用小端字节序的平台上,例如x86或x86-64的系统上,第一个字节就是0x7B。但是MIPS采用的是大端字节序,所以数权最高的这个字节排列在最后。
正是因为MIPS的硬件平台可能采用两种不同的字节序,所以MIPS Linux又分为采用大端字节序的MIPS Linux和采用小端字节序的mipsel Linux。在采取一种字节序的平台上编译出来的程序,不可能在另一种字节序的平台上运行。
本书的21.4.3节就介绍过MIPS大端字节序的特征。
ARM、PowerPC、SPARC、MIPS、IA64等CPU采用双模二元数据格式(Bi-endian),它们即可以工作于小端字节序也可以切换到大端字节序。
BSWAP指令可在汇编层面转换数据的字节序。
TCP/IP数据序的封装规范采用大端字节序,所以采用小端字节序平台的系统就需要使用专门的转换字节序的函数。
常用的字节序转换函数是htonl()和htons()。
在TCP/IP的术语里,大端字节序又称为“网络字节顺序(Network Byte Order)”,网络主机采用的字节序叫作“主机字节顺序”。x86和其他一些平台的主机字节序是小端字节序,但是IBM POWER等著名服务器系列均采用大端字节序。因此,在主机字节顺序为大端字节序的平台上使用htonl()或htons()函数转换字节序,其实不会进行真正意义上的字节重排。
[1] Debian网站提供虚拟机下载:https://people.debian.org/~aurel32/qemu/mips/。