如果“清零”是指“寄存器中的值被替换为 0,以防止我知道其他函数在做什么”,那么不,寄存器在使用前不会清零。但这并不重要,因为您是在告诉 GCC 您计划在此处存储信息,而不是您要读取当前存在的信息。
您将此信息提供给 GCC,以便(阅读文档)“您无需猜测哪些寄存器或内存位置将包含您要使用的数据”当您完成汇编代码时(例如,您不'不必记住数据是在堆栈寄存器中还是在其他寄存器中)。
GCC 需要大量的汇编代码帮助,因为“编译器……不解析汇编指令模板,也不知道它的含义,甚至不知道它是否是有效的汇编输入。扩展的 asm 特性是最常用的对于机器指令,编译器本身并不知道存在。”
更新
GCC 被设计为多遍编译器。许多通行证实际上是完全不同的程序。形成“编译器”的一组程序将您的源代码从 C、C++、Ada、Java 等转换为汇编代码。然后一个单独的程序(gas,用于 GNU Assembler)获取该汇编代码并将其转换为二进制文件(然后 ld 和 collect2 对二进制文件执行更多操作)。存在汇编块以将文本直接传递给gas,并且存在clobber-list(和输入列表),以便编译器可以进行所需的任何设置以在C、C++、Ada、Java等端之间传递信息事物和事物的gas 方面,并保证当前在寄存器中的任何重要信息都可以通过在程序集块运行之前将其复制到内存(然后从内存中复制回来)来保护免受程序集块的影响。
另一种方法是保存和恢复每个汇编代码块的每个寄存器。在具有大量寄存器的 RISC 机器上可能会变得昂贵(例如,Itanium 有 128 个通用寄存器、另外 128 个浮点寄存器和 64 个 1 位寄存器)。
我已经有一段时间没有编写任何汇编代码了。而且我使用 GCC 的命名寄存器功能比使用特定寄存器做事有更多的经验。所以,看一个例子:
#include <stdio.h>
long foo(long l)
{
long result;
asm (
"movl %[l], %[reg];"
"incl %[reg];"
: [reg] "=r" (result)
: [l] "r" (l)
);
return result;
}
int main(int argc, char** argv)
{
printf("%ld\n", foo(5L));
}
我已经要求一个输出寄存器,我将在汇编代码中调用reg,并且GCC 将在完成时自动复制到result 变量。无需在 C 代码和汇编代码中为该变量指定不同的名称;我这样做只是为了表明这是可能的。无论 GCC 决定使用哪个物理寄存器——无论是%%eax、%%ebx、%%ecx 等——当我进入汇编块时,GCC 都会负责将任何重要数据从该寄存器复制到内存中,以便我在汇编块结束之前充分使用该寄存器。
我还要求提供一个输入寄存器,我将在 C 和汇编中都将其称为 l。 GCC 承诺,当我进入汇编块时,它决定给我的任何物理寄存器都将具有当前 C 变量l 中的值。 GCC 还将做任何必要的记录保存,以保护在我进入汇编块之前碰巧在该寄存器中的任何数据。
如果我在汇编代码中添加一行怎么办?说:
"addl %[reg], %%ecx;"
由于 GCC 的编译器部分不检查汇编代码,它不会保护 %%ecx 中的数据。如果幸运的话,%%ecx 可能恰好是 GCC 决定用于%[reg] 或%[l] 的寄存器之一。如果我不走运,我会“神秘地”改变程序其他部分的值。