# 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 分别存放参数 xp 和 y
如图 3-7b 所示,函数 exchange 由三条指令实现:两个数据传送(movq),加上一条返回函数被调用点的指令(ret)。我们会在 3.7 节中讲述函数调用和返回的细节。在此之前,知道参数通过寄存器传递给函数就足够了。我们对汇编代码添加注释来加以说明。函数通过把值存储在寄存器 %rax 或该寄存器的某个低位部分中返回。
当过程开始执行时,过程参数 xp 和 y 分别存储在寄存器 %rdi 和 %rsi 中。然后,指令 2 从内存中读出 x,把它存放到寄存器 %rax 中,直接实现了 C 程序中的操作 x = *xp。稍后,用寄存器 %rax 从这个函数返回一个值,因而返回值就是 x。指令 3 将 y 写入到寄存器 %rdi 中的 xp 指向的内存位置,直接实现了操作 *xp = y。这个例子说明了如何用 MOV 指令从内存中读值到寄存器(第 2 行),如何从寄存器写到内存(第 3 行)。
关于这段汇编代码有两点值得注意。首先,我们看到 C 语言中所谓的“指针”其实就是地址。间接引用指针就是将该指针放在一个寄存器中,然后在内存引用中使用这个寄存器。其次,像 x 这样的局部变量通常是保存在寄存器中,而不是内存中。访问寄存器比访问内存要快得多。
练习题 3.4 假设变量 sp 和 dp 被声明为类型
src_t *sp;
dest_t *dp;
这里 src_t 和 dest_t 是用 typedef 声明的数据类型。我们想使用适当的数据传送指令来实现下面的操作
*dp = (dest_t) *sp;
假设 sp 和 dp 的值分别存储在寄存器 %rdi 和 %rsi 中。对于表中的每个表项,给出实现指定数据传送的两条指令。其中第一条指令应该从内存中读数,做适当的转换,并设置寄存器 %rax 的适当部分。然后,第二条指令要把 %rax 的适当部分写到内存。在这两种情况中,寄存器的部分可以是 %rax、%eax、%ax 或 %al,两者可以互不相同。
记住,当执行强制类型转换既涉及大小变化又涉及 C 语言中符号变化时,操作应该先改变大小(2.2.6 节)。
src_t | dest_t | 指令 |
|---|---|---|
long | long | movq (%rdi), %rax;movq %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 = 4C 操作符
&(称为“取址”操作符)创建一个指针,在本例中,该指针指向保存局部变量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
参数 xp、yp 和 zp 分别存储在对应的寄存器 %rdi、%rsi 和 %rdx 中。
请写出等效于上面汇编代码的 decode1 的 C 代码。