首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >隐函数声明和链接

隐函数声明和链接
EN

Stack Overflow用户
提问于 2016-01-04 19:06:55
回答 4查看 1.6K关注 0票数 4

最近,我在C中学习了隐式函数声明,其主要思想是明确的,但在这种情况下,我在理解链接过程方面遇到了一些困难。

考虑以下代码(文件a.c):

代码语言:javascript
复制
#include <stdio.h>

int main() {
    double someValue = f();
    printf("%f\n", someValue);
    return 0;
}

如果我试图编译它:

代码语言:javascript
复制
gcc -c a.c -std=c99

我看到一个关于函数f()的隐式声明的警告。

如果我试图编译和链接:

代码语言:javascript
复制
gcc a.c -std=c99

我有一个未定义的引用错误。所以一切都很好。

然后添加另一个文件(B.C文件):

代码语言:javascript
复制
double f(double x) {
    return x;
}

并调用下一个命令:

代码语言:javascript
复制
gcc a.c b.c -std=c99

令人惊讶的是,所有的东西都成功地连接起来了。当然,在./a.out调用之后,我看到了一个垃圾输出。

那么,我的问题是:具有隐式声明函数的程序是如何链接的?在我的例子中,编译器/链接器的外壳下发生了什么?

我阅读了许多主题,比如这一个,但仍然存在问题。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-01-04 19:15:59

首先,自C99以来,函数的隐式声明将从标准中删除。编译器可能支持这一点,以编译遗留代码,但这不是强制性的。引用标准前言,

  • 删除隐式函数声明

尽管如此,根据C11,第6.5.2.2节

如果函数是用不包含原型的类型定义的,且升级后的参数类型与升级后的参数类型不兼容,则行为是未定义的。

所以,就你来说,

  • 函数调用本身是隐式声明(从C99开始成为非标准声明),
  • 而且,由于函数签名隐式声明与int返回类型不匹配,所以代码调用未定义行为

为了添加更多的引用,如果在调用后尝试在同一个编译单元中定义函数,您将得到一个由于不匹配签名而产生的编译错误。

但是,在单独的编译单元中定义函数(以及缺少原型声明),编译器无法检查签名。编译后,链接器获取对象文件,并且由于链接器中没有任何类型检查(也没有对象文件中的任何信息),因此很高兴地链接它们。最后,它将最终成功地编译并链接 UB。

票数 5
EN

Stack Overflow用户

发布于 2016-01-04 19:52:11

以下是正在发生的事情。

  1. 如果没有对f()的声明,编译器就会假设一个类似于int f(void)的隐式声明。然后愉快地编译a.c
  2. 编译b.c时,编译器对f()没有任何先前的声明,因此它从f()的定义中直观地看出了这一点。通常,您会将f()的一些声明放在头文件中,并将其包括在a.cb.c中。因为两个文件都会看到相同的声明,所以编译器可以强制执行一致性。它将抱怨与声明不匹配的实体。但在这种情况下,没有通用的原型可供参考。
  3. C中,编译器不会在对象文件中存储关于原型的任何信息,链接器也不会执行任何一致性检查(它不能)。它看到的只是a.c中未解决的符号fb.c中定义的符号f。它愉快地解析符号,并完成链接。
  4. 但是,在运行时会出现故障,因为编译器根据它假设的原型在a.c中设置调用。它与b.c中的定义不匹配。f() (来自b.c)将从堆栈中获取一个垃圾参数,并将其返回为double,这将被解释为在a.c中返回时的int
票数 3
EN

Stack Overflow用户

发布于 2016-01-04 19:19:00

如何将具有隐式声明功能的方案联系起来?在我的例子中,编译器/链接器的外壳下发生了什么?

从C99开始,C标准就宣布隐式int规则为非法。因此,使用隐式函数声明的程序是无效的。

这是无效的,因为C99。在此之前,如果一个可见的原型不可用,那么编译器将隐式声明一个int返回类型。

令人惊讶的是,所有的东西都成功地连接起来了。当然,在./a.out调用之后,我看到了一个垃圾输出。

因为您没有原型,所以编译器隐式声明了一个用于int类型的f()。但是f()的实际定义返回一个double。这两种类型是不兼容的,这是https://en.wikipedia.org/wiki/Undefined_behavior

即使在隐式int规则有效的C89/C90中,这也是未定义的,因为隐式原型与f()返回的实际类型不兼容。因此,这个示例(使用a.cb.c)在所有C标准中都没有定义。

使用隐式函数声明不再有用或有效。因此,编译器/链接器处理方式的实际细节只具有历史意义。它可以追溯到K&R C的预标准时间,它没有功能原型,函数默认返回int。在C89/C90标准中将功能原型添加到C中。总之,对于有效的C程序中的所有函数,您必须有原型(或在使用之前定义函数)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34598082

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档