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

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

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

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

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

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

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

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

在1989 ANSI C标准之前,没有办法声明一个函数并指出其参数的类型。 你只需要非常小心地使每个调用与被调用的函数一致,如果你弄错了就没有编译器的警告(比如将int传递给sqrt() )。 在没有可见声明的情况下,您调用的任何函数都被假定为返回int ; 这是“隐含的”规则。 很多标准函数都会返回int ,因此你可以省去#include

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

 int c = getchar(); 

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

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

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

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

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

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

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

那么如果你在没有相应的#include情况下调用标准函数会发生什么呢?

在典型的工作环境中,这并不重要,因为运气不好,您的程序将无法在代码审查中存活下来。

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

因此,您可以研究我的这个答案,通过阅读C标准的各种版本进行跟进,找出您的编译器符合哪个版本的标准,并确定可以安全地省略#include标题的情况 -有可能在下次修改程序时弄乱一些东西。

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

首先:只包括它们。

如果不这样做,编译器将使用未声明函数的默认原型,即:

 int functionName(int argument); 

因此它将编译,并在函数可用时链接。 但是你在运行时会遇到问题。

如果遗漏标题,有很多事情你不能做:

(我希望从评论中得到更多信息,因为我的记忆失败了……)

  • 您不能使用标头中定义的任何宏。 这可能很重要。
  • 编译器无法检查您是否正在正确调用函数,因为标头为其定义了参数。

为了与旧程序兼容,C编译器可以编译代码调用尚未声明的函数,假设参数和返回值的类型为int 。 会发生什么? 例如,看看这个问题: 在C语言中将字符串转换为long long我认为如果你不包含必要的标题就可以解决这些问题的一个很好的例子,因此不要声明你使用的函数。 这个家伙发生的事情是他试图使用atoll而不包括stdlib.h在哪里宣布atoll

 char s[30] = { "115" }; long long t = atoll(s); printf("Value is: %lld\n", t); 

令人惊讶的是,这打印0 ,而不是115 ,如预期! 为什么? 因为编译器没有看到atoll的声明并假设它的返回值是一个int ,因此只选择函数在堆栈上留下的部分值,换句话说,返回值被截断。

这就是为什么建议使用-Wall编译代码的原因(所有警告都打开)。