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

错误?请详细说明。我搜索了该网站,发现了类似的问题,但找不到明确的答案。大多数答案都说过有关包含头文件的内容以消除警告,但我想知道这不是错误。

评论

默认情况下,标准C库链接到内部版本中。例如,使用gcc,您必须将-nostdlib作为传递给编译的参数,以强制其不与libc链接。

@tbert这就是链接器没有抱怨的原因,但是链接器对编译器使用C代码的作用几乎没有什么影响。

另请参见stackoverflow.com/questions/22500/…

我查找了K&R,它说如果在作用域中没有该函数的先前声明是可见的,则假定函数使用的第一个实例是具有返回类型int的声明,并且不假设该参数。感谢大家的投入。

C89,C90或C99中的所有功能是否都需要原型?

#1 楼

应该认为是错误。但是C是古老的语言,因此只是警告。
使用-Werror(gcc)进行编译可解决此问题。 int f();,这意味着该函数可以接收您提供的任何内容,并返回一个整数。如果这恰好足够接近(如果是printf,则可以),那么一切就可以正常进行。在某些情况下(例如,函数实际上返回了一个指针,并且指针比ints大),这可能会引起真正的麻烦。在这些标准中,这是一个错误。但是,默认情况下,gcc未实现这些标准,因此您仍然会收到警告。

评论


您的回答是当场正确的,并且准确地说出了K&R所说的。感谢您的简洁解释。

–火箭筒
2012年2月7日在20:22

请注意,即使在允许隐式声明的日子里,它们仍然会为诸如printf之类的可变函数生成UB。

–R .. GitHub停止帮助ICE
2012-2-7 23:42

@R ..,原则上您是对的。实际上,大多数实现都处理具有给定n个参数的可变函数,就像具有n个参数的普通函数一样,因此一切正常。

–ugoren
2012年2月8日在5:35

这个答案是24年前的正确答案。今天错了。当前的C标准(C11)和广泛使用的前一个标准(C99)都明确禁止调用未声明的函数。

–user529758
2013年12月1日0:11在

@ugoren一个非常流行的编译器,clang默认为C99,而另一个非常重要的编译器gcc支持它。市场上唯一被广泛使用的编译器,Microsoft的MSVC,是唯一不支持C89的编译器。因此,“编译器不使用较新的标准”是不正确的。

–user529758
2013年12月1日上午8:36

#2 楼

隐式声明在C中无效。

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

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

评论


与此相关的是,-pedantic-errors将使GCC(或CLang)按照此处的标准运行,并拒绝编译(请参阅:发出错误并中止)。

– TimČas
2015年2月10日在20:12

#3 楼

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

>
请参阅使gcc警告变为错误? br />

评论


相反,只要使-Werror中令人讨厌的内容静音即可。

–安蒂·哈帕拉(Antti Haapala)
17 Mar 2 '17 at 11:57

#4 楼

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

特别地,C不需要在使用函数之前对其进行声明。如果您在未声明函数的情况下对其进行了声明,则该函数的使用将成为其(隐式)声明。在我刚刚运行的一个简单测试中,这仅是对诸如printf之类的内置库函数的警告(至少在GCC中如此),但对于随机函数,它将编译得很好。

当然,当您尝试链接并且找不到foo时,您会得到一个错误。

对于诸如printf之类的库函数,某些编译器会为其包含内置声明。因此他们可以执行一些基本的类型检查,因此,当隐式声明(使用时)与内置声明不匹配时,您会收到警告。

评论


知道gcc为什么要为诸如printf之类的“内置函数”这样做吗?我似乎可以很好地使用用户定义的功能。

–火箭筒
2012年2月7日在20:34



“特别是,C不需要在使用函数之前先声明它们。” -是的。 C89(允许这样做)不是当前标准。 C11是,并且C11或其过分流行的前身C99都不允许调用隐式声明的函数。

–user529758
2013年12月1日0:13在

#5 楼

由于历史原因,可以追溯到C的第一个版本,所以假定函数具有int function(int arg1, int arg2, int arg3, etc)的隐式定义。相反,它传递参数是什么类型。因此它可能是intintdouble。没有原型,编译器将传递任何大小的参数,被调用的函数最好使用正确的参数类型来接收它。

有关更多详细信息,请查阅char*

#6 楼

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

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

如果使用错误类型或计数的参数调用该函数,则可能会发生讨厌的错误。这种情况最有可能的表现是堆栈损坏。

如今,此功能似乎显得晦涩难懂,但在过去,这是减少头文件数量的一种方法,因此编译速度更快。

评论


需要引用。我没有将隐式声明包含为改善编译时间的功能。据我所知,C(从B)的早期演变始于没有类型,但只有int,因此了解有关函数的类型信息并不那么重要...

–R .. GitHub停止帮助ICE
2012-2-7 23:44

当您说“原型”时,您的意思是“声明”。有些声明不是原型。

– M.M
17年1月31日在1:52