C语言中的隐式函数声明

C语言中的隐式函数声明,c,C,函数的隐式声明是什么意思?在不包含相应头文件的情况下调用标准库函数会产生警告,如: int main(){ printf("How is this not an error?"); return 0; } 使用一个函数而不声明它不应该是一个错误吗?请详细解释。我搜索了这个网站,发现了类似的问题,但找不到确切的答案。大多数答案都提到了包含头文件以消除警告,但我想知道这不是一个错误。由于历史原因,可以追溯到C的第一个版本,函数被假定为具有int function int arg1、int

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

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

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

由于历史原因,可以追溯到C的第一个版本,函数被假定为具有int function int arg1、int arg2、int arg3等的隐式定义

编辑:不,我对参数int的看法是错误的。相反,它传递参数的类型。所以它可以是int、double或char*。如果没有原型,编译器将传递参数的大小,被调用的函数最好使用正确的参数类型来接收它


有关更多详细信息,请查阅K&R C.

这应视为错误。但是C是一种古老的语言,所以它只是一个警告。 使用-Werror gcc编译可以修复此问题

当C找不到声明时,它假定这个隐式声明:int f;,这意味着函数可以接收你给它的任何东西,并返回一个整数。如果这恰好足够接近,并且在printf的情况下,是这样,那么事情就可以进行了。在某些情况下,例如,函数实际返回一个指针,并且指针大于int,这可能会导致真正的问题


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

C是一种非常低级的语言,因此它允许您创建几乎任何您可以想到的法律对象.o文件。你应该把C看作是一种经过修饰的汇编语言


特别是,C不需要在使用函数之前声明它们。如果您调用一个函数而不声明它,那么函数的使用将成为它的隐式声明。在我刚刚运行的一个简单测试中,这只是对内置库函数(如printf)的一个警告,至少在GCC中是这样,但对于随机函数,它可以正常编译

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


对于像printf这样的库函数,一些编译器包含它们的内置声明,以便它们可以执行一些基本类型检查,因此当使用中的隐式声明与内置声明不匹配时,您将收到警告。

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

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

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


如今,这个特性似乎是一个模糊的奇怪现象,但在过去,它是一种减少包含的头文件数量的方法,因此编译速度更快。

隐式声明在C中无效

C99删除了C89中存在的此功能


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

为了完成此图,因为-Werror可能被认为太具有侵入性,对于gcc和llvm,更精确的解决方案是在错误中仅转换此警告,使用以下选项:

-Werror=implicit-function-declaration


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

标准C库默认链接到构建中;e、 例如,对于gcc,您必须显式地将-nostlib作为参数传递给编译,以强制它不与libc链接。@tbert这就是链接器不抱怨的原因,但是链接器对编译器如何处理C代码几乎没有什么影响。另请参阅,我查阅了K&R,它说,如果在范围中没有函数的先前声明,那么函数使用的第一个实例将被假定为返回类型为int的声明,并且对参数不作任何假定。谢谢大家的意见。你们的答案的可能副本就在现场,完全符合K&R的要求。感谢您的简明解释。请注意,即使在允许隐式声明的日子里,它们仍然会导致错误
在UB中,对于诸如printf之类的可变函数,@R..,原则上你是对的。在实践中,大多数实现处理一个有n个参数的变量函数,就像一个有n个参数的普通函数一样,所以一切都是正常的。这个答案在24年前是正确的。今天是错误的。当前的C标准C11和广泛实施的以前的C99都明确禁止调用未声明的函数。@ugoren一个非常流行的编译器,clang默认为C99,另一个非常重要的编译器,gcc支持它。市场上唯一广泛使用的编译器,微软的MSVC,是唯一一个不支持C89的编译器。所以,编译器不使用较新的标准是不正确的。你知道gcc为什么对printf这样的内置函数这么做吗??我似乎能很好地处理用户定义的函数。特别是,C不要求函数在使用前声明。-是的。允许此操作的C89不是当前标准。C11是,而且C11及其过于流行的前身C99都不允许调用隐式声明的函数。需要引用。我不认为隐式声明是为了提高编译时间而包含的特性。据我所知,C从B到B的早期演化是从int开始的,没有类型,因此了解函数的类型信息并不是那么重要……你所说的原型是指声明。有些声明不是原型。另一方面,-迂腐的错误将使GCC或CLang按照这里的标准运行,并拒绝编译read:发出错误并中止。相反,只需让-Werror发出的恼人的错误保持沉默即可。