Printf和scanf可以在没有引入stdio.h的情况下工作,为什么?

3

3
@voidmain - 你的名字让我内心不舒服。 - Chris Lutz
复制:https://dev59.com/vHRC5IYBdhLWcg3wUvQS - Charles Stewart
6个回答

15
调用未声明的函数将创建一个隐式声明,基于您提供的参数和假定的返回类型为int。这使得它可以通过编译阶段,因为该函数可能存在于其他位置,直到链接时间才知道 - C并不总是有函数原型,因此这是为了向后兼容性。(在C ++中,这是一个错误,在C99 GCC中会给出警告。)
如果您查看printf,scanf,puts等的手册页(至少在FreeBSDDarwin上),它说它来自“标准C库(libc,-lc)”。GCC隐式地链接标准C库。如果您使用-nostdlib标志进行链接,则会得到您预期的“未定义符号”错误。
(事实上,当我关闭libc时,我的GNU/Linux系统还抱怨缺少_start,而我的OpenBSD系统则抱怨_start__guard__stack_smash_handler的缺失。)

第一段对于C语言是正确的,但对于C++语言则不然,只是说一下 :) - sbk

6

默认情况下,C编译器假设没有声明的函数返回一个int类型,并且不会检查函数的参数。如果能够找到声明,则编译器将进行参数检查。


1
我非常确定这只适用于C语言,而不是C++。 - user180247

1

当编译器发出警告时,你应该弄清楚这意味着什么,并解决问题。编译器警告你代码无效,然后继续编译代码的事实意义不大。

至于为什么它们能工作,答案是“运气不好”。它似乎只对你有效,但根据C标准并不保证。


他说他收到了一个警告;显然他是在启用警告的情况下运行。 - T.J. Crowder
@T.J. - 不一定。许多编译器会无条件地发出针对一些常见错误的警告。然而,增加警告级别可以使一些错误更清晰,并帮助您更早地捕捉它们。 - Chris Lutz
我猜我错误地解析了“放弃警告”的意思。这不是最好的说法,但我应该更加小心。谢谢。 - Alok Singhal

1
因为在C语言中不需要提供函数的原型,所以参数传递有标准的转换方式,因此如果你传递相同类型的参数,它应该能够工作。
回到C语言的早期,根本没有原型,后来才加入了原型。 注意:这在C++中是不同的,调用没有原型的函数会导致编译错误。

除非在作用域中有原型,否则不允许调用可变参数函数(如 printfscanf)- 这是未定义的行为。 - caf

1
在C语言中,如果你没有声明一个函数,然后你仍然使用它,那么默认情况下它返回类型为int。(顺便说一句,这些函数的返回类型也是int。)此外,在旧时代(ANSI之前),我相信无论您是否在原型中指定了参数的名称或类型,都不会影响函数的参数。因此,即使对于您的情况,printf()被声明为int printf();,您仍然可以传递参数给它而不会出错。我不确定标准中是否有任何保证,声明为这样的函数将具有与int (*)(const char *, ...)相同的调用约定,但对于大多数编译器,我猜答案是肯定的。所以它能够工作。
请注意,C++的规则是不同的。如果您将代码编译为C++,我猜测编译器将不会接受它。

0

我猜测您正在使用的是GCC编译器,它实际上“知道”一些常用函数,比如printf,并有特殊代码来处理它们。这使得它能够执行像检查printf格式字符串等C或C++标准不要求的操作。


我真的怀疑这个解释。我非常确定如果printf()没有原型,g++会拒绝使用printf()。 - asveikau
1
gcc会接受它(带有警告),g++则不会。而且GCC确实有特殊的代码来处理printf-请参阅手册中的-Wformat。 - anon
gcc尝试编译C代码,而g++编译C ++。隐式函数声明(如jleedev所描述)仅在C中合法,而不是C ++。尝试使用函数int Neil_Butterworth(int),并在使用后进行声明。在我的编译器(cygwin,gcc 3.4.4)上可以工作。好吧,要么gcc“认识”你,要么它就是隐式函数声明 :-) - Tadeusz Kopec for Ukraine
C编译器并不知道任何东西,它会推断(并希望警告)并假定一个标准的int返回值。 - stsquad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接