如果在运行C程序时不包含头文件,会发生什么情况呢?我知道会出现警告,但程序可以正常运行。我知道头文件包含函数声明,那么当我不包含它们时,编译器如何找到这些声明呢?它会检查所有的头文件吗?
printf
。它们会超越对来自 C 标准的函数进行常规参数检查 - I/O、内存管理等。例如,某些编译器会在您将 float
传递给 printf
时发出警告,而格式字符串相应位置为 %d
。但是,并非所有编译器都会这样做,也没有任何编译器需要这样做。 - Sergey Kalinichenkofloat
转换为int
用于"%d"
。它只是用它来生成一个警告。 printf("%d\n",1.25)
仍然会产生荒谬的结果。 - Keith Thompsonint
,它们被假定为与您传递的内容匹配(在参数提升之后)。因此,如果您在没有包含 #include <math.h>
的情况下调用 sqrt(1.0)
,编译器将假定 int sqrt(double)
-- 当然这仍然是错误的。这是 C90 的规定;C99 取消了 "隐式 int
" 规则。 - Keith Thompsonint
传递给sqrt()
),编译器不会发出警告。在没有可见的声明的情况下,任何你调用的函数都被认为返回int
,这就是“隐式int”规则。许多标准函数确实返回int
,所以通常可以省略#include
。int c = getchar();
getchar()
返回一个int
。
1999年的ISO C标准取消了隐式int
规则,并使在没有可见声明的情况下调用函数成为非法(实际上是一种约束违反)。因此,如果您调用标准函数而没有必需的#include
,符合C99的编译器必须发出诊断(可能只是警告)。非原型函数声明(不指定参数类型的声明)仍然是合法的,但被视为过时。
(2011年的ISO C标准在这个特定领域没有多少变化。)#include
的情况下调用标准函数,可能会发生以下情况:(a)编译器将警告您缺少声明,(b)它将假设函数返回int
并接受您实际传递的任何数量和类型的参数(还考虑类型提升,如short
到int
和float
到double
)。如果调用是正确的,并且如果您的编译器宽容,那么您的代码可能会正常工作 - 但是如果它因某种无关原因失败,则您将有更多要担心的事情。
像printf
这样的变参函数是另一回事。即使在C89/C90中,使用没有可见原型调用printf
也具有未定义的行为。编译器可以针对可变参数函数使用完全不同的调用约定,因此printf("hello")
和puts("hello")
可能会生成完全不同的代码。但是,出于与旧代码的兼容性考虑,大多数编译器使用兼容的调用约定,因此例如K&R1中的第一个“hello world”程序可能仍然可以编译和运行。
您还可以为标准函数编写自己的声明;编译器不关心它是否在标准头文件中看到声明还是在您自己的源文件中看到。但是这样做没有意义。声明从一个标准版本到下一个标准版本 subtle地发生了变化,您的实现附带的头文件应该是正确的。
那么,如果您调用一个标准函数而没有相应的#include
,会发生什么呢?-std=c99 -pedantic-errors
这种方式)实际上,大多数编译器只会打印警告。如果函数返回int
(或者如果您忽略结果),并且您正确获取了所有参数类型,则该调用可能有效。如果调用不正确,则编译器可能无法打印良好的诊断信息。如果函数不返回int
,编译器可能会假定它会返回int
,从而导致您获得垃圾结果,甚至使程序崩溃。
因此,您可以研究我的答案,接着阅读各个版本的C标准,找出您的编译器符合哪个版本的标准,并确定在什么情况下可以安全地省略#include
头文件 - 但风险是下次修改程序时可能会出错。
或者,您可以注意编译器的警告(使用可用的任何命令行选项启用),阅读每个调用的函数的文档,在每个源文件的顶部添加所需的#include
,并且不必担心任何这些问题。
int functionName(int argument);
如果函数可用,它将编译和链接。但是在运行时会出现问题。
为了与旧程序兼容,C编译器可以编译调用未声明函数的代码,假设参数和返回值的类型为int
。会发生什么?例如,看看这个问题:Troubling converting string to long long in C我认为这是一个很好的例子,说明如果您不包括必要的头文件并因此未声明使用的函数,您可能会遇到的问题。那位用户遇到的问题是他尝试使用atoll
而没有包括声明atoll
的stdlib.h
文件:
char s[30] = { "115" };
long long t = atoll(s);
printf("Value is: %lld\n", t);
令人惊讶的是,这打印出了0
,而不是预期的115
!为什么?因为编译器没有看到atoll
的声明,并假定它的返回值是一个int
,因此只选择了函数在堆栈上留下的部分值,换句话说,返回值被截断了。
这就是为什么建议使用-Wall
(所有警告)编译代码的原因之一。
如果省略头文件,你将无法做很多事情:
(我希望从评论中得到更多信息,因为我的记忆正在衰退...)