假设我有以下C代码: #include
int main()
{
int x = 11;
int y = x + 3;
printf("%d\n", x);
return 0;
}
然后我使用gcc将其编译成asm,我得到了这个(删除了一些标志):
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $11, -4(%rbp)
movl -4(%rbp), %eax
addl $3, %eax
movl %eax, -8(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
ret
我的问题是为什么movl -4(%rbp), %eax
后跟movl %eax, %esi
,而不是简单movl -4(%rbp), %esi
(根据我的实验效果很好)?
答案 0 :(得分:4)
您可能没有启用优化。
如果没有优化,编译器将生成这样的代码。例如,不将数据分配给寄存器,但是在堆栈上。这意味着当您对变量进行操作时,它们将首先被转移到寄存器然后进行操作。
因为x
生命是在-4(%rbp)
中分配的,所以这就是代码的显示方式,就像你直接翻译它而不进行优化一样。首先,您将11
移至x
的存储空间。这意味着:
movl $11, -4(%rbp)
完成第一个声明。下一个声明是评估x+3
并放入y
的存储空间(-8(%rbp)
,这是在不考虑先前生成的代码的情况下完成的:
movl -4(%rbp), %eax
addl $3, %eax
movl %eax, -8(%rbp)
完成了第二个声明。顺便说一下,它分为两部分:x+3
的评估和结果的存储。然后编译器继续为printf
语句生成代码,同样不考虑先前的语句。
另一方面,如果您启用优化,编译器会做一些智能和人类明显的事情。一件事是它允许将变量分配给寄存器,或者至少跟踪可以找到变量值的位置。在这种情况下,编译器会在第二个语句中知道x
不仅存储在-4(%ebp)
,它还会知道它存储在$11
中(是的,它表示它的实际值)。然后它可以使用它来向它添加3
,这意味着它知道结果为14
(但它更聪明的是 - 它也看到你没有使用该变量,所以它跳过了声明完全)。下一个语句是printf
语句,在这里它可以使用它知道x
为11
并将其直接传递给printf
的事实。顺便说一下,它还意识到它无法在x
使用-4(%ebp)
的存储空间。最后它可能知道printf
做了什么(因为你包含了stdio.h
)所以可以分析格式字符串并在编译时进行转换,将printf
语句替换为直接写{的调用{1}}标准化。