gpt4 book ai didi

c - 了解由于原型(prototype)不匹配而导致的意外结果 (C89)

转载 作者:太空宇宙 更新时间:2023-11-04 08:27:00 25 4
gpt4 key购买 nike

我有一个程序 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

哪位懂汇编的能帮忙找出源码问题?

最佳答案

了解为什么得到“1”需要一些 ASM 和 x86-64 ABI知识。首先,goo.cfoo.c是两个独立的编译单位。 foo.c 唯一知道的关于 foo 函数的是伪造的原型(prototype)。

伪造原型(prototype)如下:void foo(double);。这是一个功能只需要一个双参数。 x86-64 ABI 要求 double 通过 xmm 寄存器传递(确切的措辞is '如果类是 SSE,则使用下一个可用的 vector 寄存器,寄存器按照从 %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

如您所见,在到达 foo() 之前,没有设置 %rdi,它传递给 printf 的地方。这意味着 1 已传递给 main首先。现在,在问题中,main 给出以下内容原型(prototype):int main()。但是编译器实际上将函数设置为改为使用以下原型(prototype):int main (int argc, char *argv[],
字符 *envp[])
。因此存储在 %rdi 中的第一个参数实际上是参数。这就是程序打印 1 的原因。

关于c - 了解由于原型(prototype)不匹配而导致的意外结果 (C89),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30020963/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com