C 源文件包含它自己的 header 文件有什么好处

What's the benefit for a C source file include its own header file

我知道如果源文件需要引用其他文件的函数,那么它需要包含它的 header 文件,但我不明白为什么源文件包含它自己的 header文件。 header 文件中的内容只是在 per-processing 时间内作为函数声明被复制并粘贴到源文件中。对于包含自己的 header 文件的源文件,这样的 "declaration" 对我来说似乎没有必要,事实上,项目仍然可以编译并且 link 删除 header 后没问题来自它的源文件,那么源文件包含它自己的原因是什么 header?

头文件告诉人们源文件可以做什么。

所以头文件的源文件需要知道它的义务。这就是它包含在内的原因。

你的情况似乎是边缘情况,但可以将包含文件视为该源文件与可能需要这些功能的任何其他源文件之间的一种契约

通过在 header 文件中写入 "contract",您可以确保其他源文件知道如何调用这些函数,或者更确切地说,您将确定 编译器 将插入正确的代码并在编译时检查其有效性。

但是,如果您随后(甚至无意中)更改了相应源文件中的函数原型怎么办?

通过在该文件中包含与其他人相同的 header,如果无意中更改了合约,您将在编译时收到警告 "break"。

更新(来自@tmlen 的评论):即使在这种情况下没有,包含文件可能 也使用声明和编译指示,例如#defines、typedef、enum、 struct 和 inline 以及编译器宏,这样写不止一次是没有意义的(实际上,在两个不同的地方写 危险 ,以免副本与每个地方不同步其他带来灾难性的后果)。其中一些(例如 structure padding pragma)可能会成为难以追踪的错误。

主要好处是让编译器验证您的 header 及其实现的一致性。你这样做是因为它方便,而不是因为它是必需的。如果没有这样的包含,绝对有可能使项目编译并 运行 正确,但在长期 运行.

中会使项目的维护复杂化

如果您的文件不包含它自己的 header,您可能会意外遇到函数的前向声明与函数定义不匹配的情况 - 可能是因为您添加或删除了参数,忘记更新 header。发生这种情况时,依赖于不匹配函数的代码仍会编译,但调用会导致未定义的行为。最好让编译器捕获此错误,当您的源文件包含它自己的 header.

时,它会自动发生

这很有用,因为函数可以在定义之前声明。

所以碰巧你有声明,然后是 call\invocation,然后是实现。 你不必,但你可以。

头文件包含声明。只要原型匹配,您就可以随时调用。而且只要编译器在完成编译前找到实现即可。

实际示例 - 假设项目中有以下文件:

/* foo.h */
#ifndef FOO_H
#define FOO_H
double foo( int x );
#endif

/* foo.c */
int foo( int x )
{
  ...
}

/* main.c */
#include "foo.h"

int main( void )
{
  double x = foo( 1 );
  ...
}

注意foo.h中的声明与foo.c中的定义不匹配; return 类型不同。根据 foo.h 中的声明,main.c 调用 foo 函数,假设它 return 是 double

foo.cmain.c 是相互分开编译的。由于 main.c 调用了 foo,正如在 foo.h 中声明的那样,它编译成功。由于foo.c包含foo.h,编译器不知道声明和定义之间的类型不匹配,所以它也编译成功。

当您 link 将两个目标文件放在一起时,函数调用的机器代码将与函数定义的机器代码所期望的不匹配。函数调用期望 double 值被 returned,但函数定义 return 是 int。这是一个问题,尤其是当这两种类型的大小不同时。最好的情况是你得到一个垃圾结果。

通过在 foo.c 中包含 foo.h,编译器可以在您 运行 您的程序之前捕捉到这种不匹配。

而且,正如之前的回答中指出的那样,如果 foo.h 定义了 foo.c 使用的任何类型或常量,那么您肯定需要包含它。