我有一个程序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,
我得到goo.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
任何知道如何阅读汇编的人都可以帮助找出源问题吗?
答案 0 :(得分:2)
了解为什么得到'1'需要一点ASM和x86-64 ABI
知识。首先,goo.c
和foo.c
是两个单独的编译
单位。 foo.c
了解foo
函数的唯一事情是
伪造的原型。
虚假原型如下:void foo(double);
。这是一个功能
只需要一个双参数。 x86-64 ABI要求这样做
双打通过xmm
寄存器(确切的措辞
是'如果类是SSE,则使用下一个可用的向量寄存器,
寄存器按从%xmm0到%xmm7的顺序获取。'。
这意味着当编译器设置参数来调用
foo()
函数,它将通过%xmm0
传递参数。在
简化asm会发生什么:
mov 3.0, %xmm0
call foo
现在,foo()
就此而言,相信它会收到一个int。该
x86-64 ABI说:'如果该类是INTEGER,则是下一个可用的寄存器
使用序列%rdi,%rsi,%rdx,%rcx,%r8和%r9。首先
参数应该通过%rdi
传递。这意味着foo()
会做类似的事情:
mov %rdi, %rsi
mov 0xabcd, %rdi // 0xabcd being the address of the "%d" string
call printf
因此,您最终将打印%rsi
中的内容,而不是%xmm0
。
但为什么1
?您将通过发出以下命令获得一个想法:
./a.out a
./a.out a b
./a.out a b c
看模式?让我们回到简化的程序集:
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
正如您所看到的,在%rdi
到达foo()
之前,没有任何内容正在设置1
,
它传递给printf的地方。这意味着main
被传递给main
首先。现在,在这个问题中,int main()
给出了以下内容
原型:int main (int argc, char *argv[],
char *envp[])
。但编译器实际上将函数设置为
请改为使用以下原型:%rdi
。实际上存储在argc
中的第一个参数
1
。这就是程序正在打印Failed to contact JobTracker plugin at None:9290
。