我有一个程序goo.c
void foo(double);
#include <stdio.h>
void foo(int x){
printf ("in foo.c:: x= %d\n",x);
}
由foo.c调用
int main(){
double x=3.0;
foo(x);
}
我编译并运行
gcc foo.c goo.c
./a.out
你猜怎么着?结果得到“x=1”。然后我发现“foo”的签名应该是
void foo(int)
。显然,我的双输入值3.0必须向下转换为int。但是,如果我尝试使用测试程序查看(int)3.0的值:int main(){
double x=3.0;
printf ("%d", ((int) x));
}
我得到3作为输出,这使得前面的“x=1”更加难以理解。知道吗?有关信息,我的gcc是用ANSI C标准运行的。谢谢。
[编辑]如果我按照JS1的建议使用gcc-S,
我有点不舒服
.file "goo.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movabsq $4613937818241073152, %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
call foo
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
和foo.s
.file "foo.c"
.section .rodata
.LC0:
.string "in foo.c:: x= %d\n"
.text
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
任何知道如何读取程序集的人都可以帮助解决源问题?
最佳答案
理解为什么得到“1”需要一些ASM和x86-64 ABI
知识。首先,goo.c
和foo.c
是两个独立的编译
单位。关于foo.c
函数,foo
唯一知道的是
伪造的原型。
伪造的原型如下:void foo(double);
。这是一个函数
这只需要一个双参数。x86-64 ABI要求
双倍通过xmm
寄存器(确切的短语
如果类是SSE,则使用下一个可用的向量寄存器,
寄存器按从%xmm0到%xmm7的顺序获取。
这意味着当编译器设置参数来调用
函数,它将通过foo()
传递参数。在
简化的asm发生的情况是:
mov 3.0, %xmm0
call foo
现在,
%xmm0
,在它的一边,相信它会得到一个intx86-64abi说:“如果类是整数,那么下一个可用的寄存器
使用了序列%rdi、%rsi、%rdx、%rcx、%r8和%r9。第一次
参数应该通过
foo()
传递。这意味着%rdi
会做如下事情:
mov %rdi, %rsi
mov 0xabcd, %rdi // 0xabcd being the address of the "%d" string
call printf
所以你最终会打印出
foo()
中的内容,而不是%rsi
。但是为什么?您将通过发出以下命令获得一个想法:
%xmm0
1
./a.out a
看到模式了吗?让我们回到简化的程序集:
main:
mov 3.0, %xmm0
call foo
ret
foo:
mov %rdi, %rsi
mov 0xabcd, %rdi // 0xabcd being the address of the "%d" string
call printf
ret
如您所见,在达到
./a.out a b
之前,没有设置./a.out a b c
,传递给printf的地方。这意味着
%rdi
被传递到foo()
首先。现在,在这个问题中,
1
给出如下原型:
main
。但是编译器实际上将函数设置为改为使用以下原型:
main
。因此存储在int main()
中的第一个参数实际上是int main (int argc, char *argv[],char *envp[])
。这就是程序打印%rdi
的原因。关于c - 了解由于原型(prototype)不匹配而导致的意外结果(C89),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30020963/