# 3.4.3 数据传送示例

作为一个使用数据传送指令的代码示例,考虑图 3-7 中所示的数据交换函数,既有 C 代码,也有 GCC 产生的汇编代码。

long exchange(long *xp, long y)
{
    long x = *xp;
    *xp = y;
    return x;
}

a)C 语言代码

long exchange(long *xp, long y)
xp in %rdi, y in %rsi

1  exchange:
2      movq (%rdi), %rax    Get x at xp. Set as return value.
3      movq %rsi, (%rdi)    Store y at xp.
4      ret                  Return.

b)汇编代码

图 3-7 exchange 函数的 C 语言和汇编代码。寄存器 %rdi%rsi 分别存放参数 xpy

如图 3-7b 所示,函数 exchange 由三条指令实现:两个数据传送(movq),加上一条返回函数被调用点的指令(ret)。我们会在 3.7 节中讲述函数调用和返回的细节。在此之前,知道参数通过寄存器传递给函数就足够了。我们对汇编代码添加注释来加以说明。函数通过把值存储在寄存器 %rax 或该寄存器的某个低位部分中返回。

当过程开始执行时,过程参数 xpy 分别存储在寄存器 %rdi%rsi 中。然后,指令 2 从内存中读出 x,把它存放到寄存器 %rax 中,直接实现了 C 程序中的操作 x = *xp。稍后,用寄存器 %rax 从这个函数返回一个值,因而返回值就是 x。指令 3 将 y 写入到寄存器 %rdi 中的 xp 指向的内存位置,直接实现了操作 *xp = y。这个例子说明了如何用 MOV 指令从内存中读值到寄存器(第 2 行),如何从寄存器写到内存(第 3 行)。

关于这段汇编代码有两点值得注意。首先,我们看到 C 语言中所谓的“指针”其实就是地址。间接引用指针就是将该指针放在一个寄存器中,然后在内存引用中使用这个寄存器。其次,像 x 这样的局部变量通常是保存在寄存器中,而不是内存中。访问寄存器比访问内存要快得多。

练习题 3.4 假设变量 spdp 被声明为类型

src_t *sp;
dest_t *dp;

这里 src_tdest_t 是用 typedef 声明的数据类型。我们想使用适当的数据传送指令来实现下面的操作

*dp = (dest_t) *sp;

假设 spdp 的值分别存储在寄存器 %rdi%rsi 中。对于表中的每个表项,给出实现指定数据传送的两条指令。其中第一条指令应该从内存中读数,做适当的转换,并设置寄存器 %rax 的适当部分。然后,第二条指令要把 %rax 的适当部分写到内存。在这两种情况中,寄存器的部分可以是 %rax%eax%ax%al,两者可以互不相同。

记住,当执行强制类型转换既涉及大小变化又涉及 C 语言中符号变化时,操作应该先改变大小(2.2.6 节)。

src_t dest_t 指令
long long movq (%rdi), %raxmovq %rax, (%rsi)
char int
char unsigned
unsigned char long
int char
unsigned unsigned char
char short

旁注:指针的一些示例

函数 exchange(图 3-7a)提供了一个关于 C 语言中指针使用的很好说明。参数 xp 是一个指向 long 类型的整数的指针,而 y 是一个 long 类型的整数。语句

long x = *xp;

表示我们将读存储在 xp 所指位置中的值,并将它存放到名字为 x 的局部变量中。这个读操作称为指针的间接引用(pointer dereferencing),C 操作符 * 执行指针的间接引用。语句

*xp = y;

正好相反——它将参数 y 的值写到 xp 所指的位置。这也是指针间接引用的一种形式(所以有操作符 *),但是它表明的是一个写操作,因为它在赋值语句的左边。

下面是调用 exchange 的一个实际例子:

long a = 4;
long b = exchange(&a, 3);
printf("a = %ld, b = %ld\n", a, b);

这段代码会打印出:

a = 3, b = 4

C 操作符 &(称为“取址”操作符)创建一个指针,在本例中,该指针指向保存局部变量 a 的位置。然后,函数 exchange 将用 3 覆盖存储在 a 中的值,但是返回原来的值 4 作为函数的值。注意如何将指针传递给 exchange,它能修改存在某个远处位置的数据。

练习题 3.5 已知信息如下。将一个原型为

void decode1(long *xp, long *yp, long *zp);

的函数编译成汇编代码,得到如下代码:

void decode1(long *xp, long *yp, long *zp)
xp in %rdi, yp in %rsi, zp in %rdx

decode1:
    movq (%rdi), %r8
    movq (%rsi), %rcx
    movq (%rdx), %rax
    movq %r8, (%rsi)
    movq %rcx, (%rdx)
    movq %rax, (%rdi)
    ret

参数 xpypzp 分别存储在对应的寄存器 %rdi%rsi%rdx 中。

请写出等效于上面汇编代码的 decode1 的 C 代码。