【问题标题】:Why doesn't linking a function of the wrong type return garbage?为什么链接错误类型的函数不返回垃圾?
【发布时间】:2021-12-25 20:41:09
【问题描述】:

我正在浏览 K&R C 的书,第 4.2 章说如下:

如果 atof 本身和 main 中对它的调用在同一个源文件中的类型不一致,编译器会检测到错误。但是,如果(更可能)atof 是单独编译的,则不会检测到不匹配,atof 将返回一个 double,main 将其视为 int,并且会产生无意义的答案。

我想亲眼看看如果我创建并不一致地使用他们描述的函数会发生什么:

main.c:

#include <stdio.h>

int floatFunction();

int main() {
    int result = floatFunction();
    printf("result: %d\n", result);
}

floatFunction.c:

float floatFunction() {
    return 10;
}

重击:

$ gcc main.c floatFunction.c -o main
$ ./main
result: 0
$

无论我得到什么值 floatFunction 返回,我总是得到 0 结果。我还尝试使用-o3 进行编译,结果相同。我期待看到 10.0 的位模式被解释为 int 结果。为什么这没有发生?

另外,为什么编译过程没有通知我类型不匹配,我该如何保护自己在实际代码中不犯这样的错误?

【问题讨论】:

  • 你必须在你的hello.c中声明来自单独编译单元的float floatFunction()才能使用它。
  • 编译器一次只能看到一个翻译单元,它可以检查单个文件中类型不匹配的不一致。查看链接器:stackoverflow.com/questions/9072459/…
  • 你应该看看反汇编看看发生了什么。
  • K&R 不可能知道每个编译器或语言版本会做什么,因为这是一个很大的移动目标。然后是 ISA,这无疑会引起您的困惑。
  • 回答标题问题:A 0 垃圾。

标签: c gcc


【解决方案1】:

至少在 x86 上有不同的寄存器用于传递(和返回)浮点值和整数值(指针和整数)。

为什么你在整数值的返回寄存器中始终有整数零,而浮点值的返回寄存器用于真正的返回值,我不知道。可能只是运气。

有关 Linux 调用约定的更多信息,请参见 What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64

无论如何,请记住,无论你观察到什么,至少在语言层面上,它只是未定义的行为,任何事情都会发生。

【讨论】:

  • “可能只是运气。” -- 可能不会。
  • 也许不走运,但零 afaics 的调用约定没有理由。
  • 如果使用硬浮点,则使用不同的寄存器,除非您做一些工作,否则您通常会在 x86 工具上获得这些。如果软浮动那么可能/很可能您可以更改返回值并看到与 main() 不同的东西
【解决方案2】:

我期待看到 10.0 的位模式被解释为 int 结果。为什么这没有发生?

最可能的原因是在您的平台上(您没有指定)浮点值在与返回整数值的寄存器不同的寄存器中返回。

例如,在x86_64 上,整数结果在rax 寄存器中返回,而双精度和浮点结果在xmm0 寄存器中返回。

这是关于各种 x86 调用约定的 article

如果您不在 x86_64 上,则需要查找您平台的调用约定。

还有,为什么编译过程没有通知我类型不匹配

编译过程一次只能看到一个源文件(编译单元),所以它不会警告你。

链接器可以,但实际上只有 AIX 链接器可以(我知道的那些)。

如何保护自己在实际代码中不犯这样的错误?

通过在头文件中声明您的函数并将该头文件包含在两个编译单元中。

【讨论】:

    猜你喜欢
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2013-09-04
    • 1970-01-01
    相关资源
    最近更新 更多