48

术语“函数的隐式声明”是什么意思?在不包含适当头文件的情况下调用标准库函数会产生警告,例如:

int main(){
  printf("How is this not an error?");
  return 0;
}

不应该在不声明它是错误的情况下使用函数吗?请详细说明。我搜索了这个网站,发现了类似的问题,但找不到明确的答案。大多数答案都说包括头文件以消除警告,但我想知道这不是错误。

4

6 回答 6

85

它应该被认为是一个错误。但是 C 是一种古老的语言,所以它只是一个警告。
-Werror(gcc) 编译可以解决这个问题。

当 C 没有找到声明时,它假定这个隐式声明: int f();,这意味着函数可以接收你给它的任何东西,并返回一个整数。如果这恰好足够接近(并且在 的情况下printf,它是),那么事情就可以工作了。在某些情况下(例如函数实际上返回一个指针,并且指针大于整数),它可能会造成真正的麻烦。

请注意,这已在较新的 C 标准(C99、C11)中得到修复。在这些标准中,这是一个错误。但是,gcc默认情况下不实施这些标准,因此您仍然会收到警告。

于 2012-02-07T19:49:41.973 回答
20

隐式声明在 C 中无效。

C99 删除了此功能(存在于 C89 中)。

gcc选择默认情况下只发出警告,-std=c99但编译器有权拒绝翻译这样的程序。

于 2012-02-07T20:05:46.420 回答
7

为了完成图片,由于-Werror可能被认为过于“侵入性”,
对于 gcc(和 llvm),更精确的解决方案是使用以下选项将此警告转换错误:

-Werror=implicit-function-declaration

请参阅使一个 gcc 警告成为错误?

关于 的一般使用-Werror:当然,建议使用无警告代码,但在某些开发阶段,它可能会减慢原型设计。

于 2016-09-30T08:43:48.830 回答
4

由于历史原因可以追溯到 C 的第一个版本,因此假定函数具有隐式定义int function(int arg1, int arg2, int arg3, etc).

编辑:不,我int的论点错了。相反,它传递参数的任何类型。所以它可能是一个int或一个double或一个char*。如果没有原型,编译器将传递任何大小的参数,并且被调用的函数最好使用正确的参数类型来接收它。

更多详情请查阅K&R C

于 2012-02-07T19:48:43.237 回答
3

C 是一种非常低级的语言,因此它允许您创建几乎任何您能想到的合法对象 (.o) 文件。您应该将 C 视为基本修饰过的汇编语言。

特别是,C 不需要在使用函数之前声明它们。如果您在没有声明的情况下调用函数,则该函数的使用将成为它的(隐式)声明。在我刚刚运行的一个简单测试中,这只是对于像 printf 这样的内置库函数(至少在 GCC 中)的警告,但对于随机函数,它会编译得很好。

当然,当你尝试链接时,它找不到 foo,那么你会得到一个错误。

对于像 printf 这样的库函数,一些编译器包含它们的内置声明,因此它们可以进行一些基本的类型检查,所以当隐式声明(来自使用)与内置声明不匹配时,你会收到警告。

于 2012-02-07T19:50:07.530 回答
3

隐式声明的函数既没有原型也没有定义,但在代码中的某处被调用。因此,编译器无法验证这是函数的预期用途(参数的计数和类型是否匹配)。解析对它的引用是在编译后在链接时完成的(与所有其他全局符号一样),因此从技术上讲,跳过原型不是问题。

假定程序员知道他在做什么,这是省略提供原型的正式合同的前提。

如果使用错误类型或计数的参数调用函数,可能会发生令人讨厌的错误。最可能的表现是堆栈损坏。

现在这个特性可能看起来很奇怪,但在过去,它是一种减少包含的头文件数量的方法,因此编译速度更快。

于 2012-02-07T19:53:07.497 回答