在x86_64 ABI中,如果函数具有可变参数,那么AL
(它是EAX
的一部分)预计会保存用于保存该函数参数的向量寄存器的数量。
在您的例子:
printf("%d", 1);
具有整数参数,所以没有必要为一个矢量寄存器,因此AL
被设置为0。
在另一方面,如果你改变你的例子:
printf("%f", 1.0f);
那么浮点文字存储在一个向量寄存器,相应地,AL
设置为1
:
movsd LC1(%rip), %xmm0
leaq LC0(%rip), %rdi
movl $1, %eax
call _printf
正如预期的那样:
printf("%f %f", 1.0f, 2.0f);
将导致编译器设置AL
到2
因为有两个浮点参数:
movsd LC0(%rip), %xmm0
movapd %xmm0, %xmm1
movsd LC2(%rip), %xmm0
leaq LC1(%rip), %rdi
movl $2, %eax
call _printf
至于你的其他问题:
puts
被调用,虽然之前说得也归零%eax
只只需一个指针。为什么是这样?
它不应该。例如:
#include <stdio.h>
void test(void) {
puts("foo");
}
时gcc -c -O0 -S
,输出编译:
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
call _puts
leave
ret
和%eax
不归零。但是,如果您删除#include <stdio.h>
然后将所得的组件不零出%eax
正确调用puts()
前:
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
movl $0, %eax
call _puts
leave
ret
的原因是与你的第二个问题:
这对我自己的任何调用之前也发生void proc()函数(即使使用-O2设置),但在调用void proc2(int param)函数时不会归零。
如果编译器没有看到某个函数的声明,那么它不会对其参数做任何假设,并且函数可以接受可变参数。如果你指定一个空的参数列表(你不应该这样做,它被ISO/IEC标记为一个过时的C特性)也是如此。由于编译器没有足够的关于函数参数的信息,因此在调用函数之前会将%eax
置零,因为可能会将函数定义为具有可变参数。
例如:
#include <stdio.h>
void function() {
puts("foo");
}
void test(void) {
function();
}
其中function()
有一个空的参数列表,结果:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
call _function
leave
ret
但是,如果按照建议指定void
的实践中当函数不接受任何参数,如:
#include <stdio.h>
void function(void) {
puts("foo");
}
void test(void) {
function();
}
the n中的编译器知道function()
不接受参数 - 特别是,它不接受变量参数 - 因此并不清楚%eax
调用该函数之前:
pushq %rbp
movq %rsp, %rbp
call _function
leave
ret
'puts'也归零'% eax'就在通话之前,尽管它只需要一个指针。为什么是这样? – sh54 2011-06-02 20:00:25
这也发生在任何调用我自己的'void proc()'函数(即使使用-O2集合)之前,但是在调用'void proc2(int param)'函数时它不会被调零。 – sh54 2011-06-02 20:07:06
为了记录,它发生在调用'void proc()'之前,因为C中的签名实际上没有提及proc的arity,它也可能是一个可变参数函数,所以调零rax是必要的。 'void proc()'与'void proc(void)'不同。请参阅http://stackoverflow.com/questions/693788/-void-arguments – frangio 2015-04-21 13:27:45