如果我不包含头文件会发生什么

Des*_*PRG 3 c header

如果我在运行ac程序时不包含头文件会发生什么?我知道我得到警告,但程序运行完美.

我知道头文件包含函数声明.因此,当我不包含它们时,编译器如何解决它?它会检查所有头文件吗?

das*_*ght 7

我知道我得到警告,但程序运行完美.

这是ANSI C之前遗留下来的遗憾:该语言不需要函数原型,因此标准C允许它到今天(通常,可以生成警告以查找没有原型的函数).

当您调用没有原型的函数时,C编译器会对所调用的函数进行假设:

  • 函数的返回类型假定为 int
  • 假设所有参数都被声明(即没有...vararg东西)
  • 假设所有参数都是默认促销后传递的任何参数,依此类推.

如果没有原型调用的函数符合这些假设,那么您的程序将正确运行; 否则,它是未定义的行为.


Kei*_*son 5

在 1989 年 ANSI C 标准之前,没有办法声明函数并指示其参数的类型。您只需要非常小心地使每个调用与被调用的函数保持一致,如果出错(例如传递intto sqrt()),编译器不会发出警告。在没有可见声明的情况下,您调用的任何函数都被假定为 return int;这是“隐式整数”规则。许多标准函数确实会返回int,因此您通常可以通过省略#include.

1989 年的 ANSI C 标准(本质上也是 1990 年的 ISO C 标准)引入了原型,但没有强制要求它们(现在仍然不是)。所以如果你打电话

int c = getchar();
Run Code Online (Sandbox Code Playgroud)

它实际上会起作用,因为getchar()返回一个int.

1999 年 ISO C 标准放弃了隐含int规则,并使调用没有可见声明的函数成为非法(实际上是违反约束)。因此,如果您在没有 required 的情况下调用标准函数,则#include符合 C99 的编译器必须发出诊断信息(可能只是警告)。非原型函数声明(不指定参数类型的声明)仍然是合法的,但它们被认为是过时的。

(2011 年 ISO C 标准在这个特定领域没有太大变化。)

但是仍然有大量代码是为 C90 编译器编写的,而且大多数现代编译器仍然支持旧标准。

因此,如果您在没有 required 的情况下调用标准函数#include,则可能会发生的情况是(a)编译器会警告您缺少声明,并且(b)它将假设该函数返回int并采用任何数字和类型您实际传递的参数(也考虑了类型提升,例如shorttointfloatto double)。如果调用是正确的,并且如果您的编译器是宽松的,那么您的代码可能会工作——但是如果它失败,您还有一件事要担心,也许是由于某些不相关的原因。

像可变参数函数printf是另一回事。即使在 C89/C90 中,printf没有可见原型的调用也有未定义的行为。编译器可以使用可变参数的功能是完全不同的调用约定,所以printf("hello")puts("hello")可能产生完全不同的代码。但同样,为了与旧代码兼容,大多数编译器使用兼容的调用约定,因此例如 K&R1 中的第一个“hello world”程序可能仍会编译和运行。

您还可以为标准函数编写自己的声明;编译器不关心它是在标准头文件中还是在您自己的源文件中看到声明。但这样做没有意义。声明已从标准的一个版本微妙地更改为下一个版本,并且您的实现附带的标头应该是正确的标头。

那么如果你调用一个没有相应的标准函数,实际会发生什么#include呢?

在典型的工作环境中,这无关紧要,因为运气好的话,您的程序将无法通过代码审查。

原则上,任何符合 C99 或更高版本的编译器都可能会拒绝您的程序并显示致命错误消息。(gcc 会以这种方式运行-std=c99 -pedantic-errors) 实际上,大多数编译器只会打印警告。如果函数返回(或者如果您忽略结果)并且您获得所有参数类型正确,则调用可能会起作用。如果调用不正确,编译器可能无法打印良好的诊断信息。如果函数没有 return ,编译器可能会假设它有,你会得到垃圾结果,甚至让你的程序崩溃。intint

所以你可以研究我的这个答案,通过阅读 C 标准的各种版本来跟进,找出你的编译器符合标准的确切版本,并确定你可以安全地省略#include标头的情况 - 使用冒着下次修改程序时会搞砸的风险。

或者您可以注意编译器的警告(您已使用任何可用的命令行选项启用),阅读您调用的每个函数的文档,#include在每个源文件的顶部添加所需的s,而不必担心任何这些东西。