2.5.1 大小端

让我们用刚刚写的方法来输出内存中存储的值。我们会用两种不同的方法来达到这个目的:第一个种会分开输出它的各个字节,第二种则是按照正常整体输出。参见列表 2-11:

Listing 2-11.endianness.asm

section .data
demo1: dq 0x1122334455667788
demo2: db 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88

section .text

_start:
    mov rdi, [demo1]
    call print_hex
    call print_newline

    mov rdi, [demo2]
    call print_hex
    call print_newline

    mov rax, 60
    xor rdi, rdi
    syscall

运行这个程序,输出的结果可能让我们有点儿意外,我们在 demo1 和 demo2 中得到了完全不同的结果。

> ./main
1122334455667788
8877665544332211

可以看到,多字节的数值是以相反的顺序进行存储的!

每一个字节内的数值顺序倒是比较直观,但是多个字节则是从小一直存储到大了。

这种场景只适用于内存操作:在寄存器中的字节存储是比较自然的顺序。不同的处理器对于数值的存储方式有不同的约定。

  • 大端模式下多字节数是从最大的字节开始存储的。
  • 小端模式下多字节数是从最小的字节开始存储的。

像示例中展示的一样,Intel 64 遵循的是小端的规范。一般来说,选择某种规范只是硬件工程师的一个选择。

上面的约定和字符串或者数组的存储方式没有什么关系。不过如果每一个字符都是由两个而不是一个字节编码而成的话,这些字节会被以反序存储。

小端模式有一些优点,我们可以直接丢弃掉数字的高位来把数字从大范围数转为小范围数,例如 8 字节数转为 4 字节数。

例如,demo3:dq 0x1234。然后为了把这个数字转成 dw,我们需要从 demo3 相同的地址读取一个 dword 数字。参见表 2-1,该数字在内存中的布局详情。

Table 2-1.小端和大端模式下 qword 数字 0x1234 的内存布局情况

ADDRESS VALUE-LE VALUE-BE
demo3 0x34 0x00
demo3 + 1 0x12 0x00
demo3 + 2 0x00 0x00
demo3 + 3 0x00 0x00
demo3 + 4 0x00 0x00
demo3 + 5 0x00 0x00
demo3 + 6 0x00 0x12
demo3 + 7 0x00 0x32

大端比较常用的是网络包的传输场景(e.g. TCP/IP)。同时大端还是 JAVA 虚拟机的内部数字格式。

中端模式这个概念比较少见。假设我们现在有一些算术过程,需要对 128 位的数值进行计算。那么其字节是像这样存储:先是 8 个倒序的低位字节,然后是 8 个倒序的高位字节:

7 6 5 4 3 2 1 0, 16 15 14 13 12 11 10 9 8

results matching ""

    No results matching ""