C程序没有头文件

3

我用C语言写了一个“Hello World”程序。

void main()
{ printf("Hello World"); }
// note that I haven't included any header file

该程序编译时产生警告,错误信息如下:

vikram@vikram-Studio-XPS-1645:~$ gcc hello.c 
hello.c: In function ‘main’:
hello.c:2:2: warning: incompatible implicit declaration of built-in function ‘printf’
vikram@vikram-Studio-XPS-1645:~$ ./a.out 
Hello Worldvikram@vikram-Studio-XPS-1645:~$

这怎么可能?操作系统如何在不包含任何头文件的情况下链接库?
5个回答

7
printf函数位于C库中(在您的情况下是libc),它隐式链接(实际上gcc有一个printf内置函数,但这不是重点)。
包含头文件并不会为链接器带来任何函数,它只是告诉编译器它们的声明(即“它们看起来像什么”)。
显然,您应该始终包含头文件,否则你就会强制编译器做出关于函数外观的假设。

7
编译器在生成代码时会引用一个名为printf()的函数,但并不知道它所需的参数类型和返回值类型。生成的汇编代码包含了将字符串"Hello World"的地址推入程序静态数据区域的push指令,紧接着是对printf函数的call指令。
当将目标文件链接成可执行文件时,连接器会看到对printf的引用,并提供C标准库函数printf()。由于你传递的参数(const char*)与真正的printf()声明相匹配,因此它可以正确地运行。但请注意,程序隐式声明的printf()的返回类型是int (我想),这也是标准printf()的返回类型。如果它们不同,并且您将调用printf()的结果赋给变量,则会进入未定义行为的领域,您可能会得到不正确的值。
简而言之:要使用正确的函数声明,请包含正确的头文件,因为这种隐式声明已被弃用,因为容易出错。

1

头文件通常只包含函数声明、符号常量和宏定义;通常不包括函数定义。

stdio.h 提供的只是 printf 的原型声明:

int printf(const char * restrict format, ...); // as of C99

printf的实现是在一个单独的库文件中,您的代码链接该文件。

您的代码“工作”的原因有两个:

  1. 在C89和早期版本下,如果编译器在函数声明或定义之前看到函数调用,它将假定该函数返回int并且带有未指定数量的参数;

  2. printf的实现返回一个int,而您传递了一个参数,恰好与printf的实现对于第一个参数所需的兼容。

还要重申大家的建议,使用int main(void)int main(int argc, char **argv);除非您的编译器文档明确列出void main()作为合法签名,否则使用它将会引发未定义行为(这意味着从您的代码运行时没有明显问题到退出崩溃到完全无法加载的所有情况都有可能发生)。


我说“通常”; 我遇到过一些包含代码的头文件,但那些通常是由不知道自己在做什么的人编写的。可能有非常罕见的情况下,将代码放在头文件中是合理的,但作为一种规则,这是不好的实践。

1
在C语言中,如果你使用标准库函数,你必须包含声明该函数的标准头文件。例如,对于printf函数,你必须包含stdio.h头文件。
在C89(以及默认情况下使用的GNU C89)中,有时可以省略函数声明,因为有一个称为隐式函数声明的特性:当使用函数标识符foo且未声明该函数时,实现将使用此声明:
 /* foo is a function with an unspecified number of arguments */
extern int foo();

但这个声明仅适用于返回具有未指定但固定数量的参数的 int 的函数。如果函数接受可变数量的参数(例如 printf),这样的程序将调用未定义的行为。

以下是 C89/C90 的规定:

(C90, 6.7.1) "如果定义了一个接受可变数量参数的函数,并且没有以省略号符号结束的参数类型列表,那么其行为是未定义的。

因此,gcc 足够友善,可以在 C89 和 GNU C89 中编译:编译器也可能会拒绝编译。

还要注意的是:

void main() { ... }

main不是一个有效的定义(至少在托管实现中,这可能是您的情况)。

如果您的主函数不带任何参数,请使用此有效的定义:

int main(void) { ... }

0
hello.c:2:2: 警告:‘printf’内建函数的声明不兼容
为了解决此警告,您应该包含头文件(stdio.h)。 您不小心使用了自1999年以来已被弃用的旧C功能。
此外,链接未失败的事实仅意味着默认情况下链接了标准C库。您是否包含了相关的头文件是无关紧要的。

我认为对于像 printf 这样带有可变参数的函数,隐式声明 extern int printf(); 在C89中甚至是无效的。 gcc 可以友善地在c89中进行编译。 - ouah

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