如何在C中构造#includes

假设我有一个C程序,它被破坏为一组* .c和* .h文件。 如果一个文件中的代码使用另一个文件中的函数,那么我应该在哪里包含头文件? 在使用该函数的* .c文件中,或在该文件的标题内?

例如,文件foo.c包含foo.h ,其中包含foo.c所有声明; 对于bar.cbar.hfoo.c函数foo1()调用bar1() ,它在bar.h声明并在bar.c定义。 现在问题是,我应该在foo.h包含bar.h还是在foo.c

对于这些问题,一套好的经验法则是什么?

你应该在foo.c中包含foo.h. 这样,包含foo.h的其他c文件将不会不必要地携带bar.h。 这是我对包含头文件的建议:

  • 在c文件中添加包含定义 – 这样在读取代码时文件依赖性更加明显。
  • 将foo.h拆分为两个单独的文件,比如foo_int.h和foo.h. 第一个只携带foo.c所需的类型和前向声明。 foo.h包含外部模块所需的function和类型。 这就像foo的私人和公共部分。
  • 避免交叉引用,即foo引用bar和bar引用foo。 这可能会导致链接问题,也是设计糟糕的表现

正如其他人所说,标题foo.h应该声明能够使用源文件foo.c提供的function所必需的信息。 这将包括foo.c提供的类型,枚举和函数。 (你不使用全局变量,不是吗?如果你这样做,那么那些也在foo.h中声明。)

标题foo.h应该是自包含的和幂等的。 自包含意味着任何用户都可以包含foo.h而不需要担心可能需要哪些其他头文件(因为foo.h包含这些头文件)。 幂等意味着如果标题包含多次,则不会造成任何损害。 这是通过经典技术实现的:

  #ifndef FOO_H_INCLUDED #define FOO_H_INCLUDED ...rest of the contents of foo.h... #endif /* FOO_H_INCLUDED */ 

问的问题是:

文件foo.c包含foo.h,其中包含foo.c的所有声明; 对于bar.c和bar.h也是如此。 foo.c中的函数foo1()调用bar1(),它在bar.h中声明并在bar.c中定义。 现在问题是,我应该在foo.h中包含bar.h还是在foo.c中包含?

这取决于foo.h提供的服务是否依赖于bar.h。 如果使用foo.h的其他文件需要bar.h定义的类型或枚举之一才能使用foo.h的function,那么foo.h应该确保包含bar.h(通过包含它)。 但是,如果bar.h的服务仅在foo.c中使用,并且使用foo.h的人不需要,则foo.h不应包含bar.h

我只会在头文件本身所需的* .h文件中包含头文件。 在我看来,源文件所需的头文件应该包含在源文件中,以便从源代码中明显看出依赖关系。 应该构建头文件以处理多个包含,因此如果需要清楚,可以将它放在两者中。

我在.h文件中包含了最小的标头集,并将其余部分包含在.c文件中。 这有利于减少编译时间。 举个例子,如果foo.h真的不需要bar.h但是还是包含它,而其他一些文件包含foo.h ,那么如果bar.h发生变化,那么该文件将被重新编译,即使它实际上可能不需要或者使用bar.h

.h文件应该定义.c文件中函数的公共接口(也就是api)。

如果file1的接口使用file2的接口,则在file1.h中使用#include file2.h

如果file1.c中的实现使用了file2.c中的东西,那么file1.c应该#include file2.h。

我必须承认 – 因为我总是#include file1.h在file1.c中 – 我通常不会在file1.c中直接#including file2.h,如果它已经在file1.h中#included

如果您发现自己处于两个.c文件#include彼此.h文件的情况,那么这表明模块化已经崩溃,您应该考虑重组一些事情。

使用foo.cfoo.h的示例我发现这些指南foo.h用:

  • 请记住, foo.h的目的是为了方便foo.c的使用,所以尽量保持简单,有条理和不言自明。 请自由发表评论,解释如何以及何时使用foo.c的function – 何时使用它们。

  • foo.h声明了foo.c的公共特性:functions,macros,typedef和( shudder )全局变量。

  • foo.c应该#include "foo.h – 请参阅讨论,以及Jonathan Leffler在下面的评论。

  • 如果foo.c需要额外的头文件来编译,请将它们包含在foo.c

  • 如果foo.h需要外部头文件来编译,请将它们包含在foo.h

  • 利用预处理器来防止foo.h被多次包含。 (见下文。)

  • 如果由于某种原因需要外部头文件以便另一个.c文件使用foo.c的function,请在foo.h包含头文件以保存下一个开发人员免于不必要的调试。 如果您反对这一点,请考虑添加宏,如果未包含所需的标头,将在编译时显示指令。

  • 除非您有充分的理由并将其清楚地记录下来,否则请勿在另一个.c文件中包含.c文件。

正如kgiannakakis所指出的那样,将公共接口与仅在foo.c本身内所需的定义和声明分开是有帮助的。 但是,不是创建两个文件,有时候让预处理器为您执行此操作会更好:

 // foo.c #define _foo_c_ // Tell foo.h it's being included from foo.c #include "foo.h" . . . 

 // foo.h #if !defined(_foo_h_) // Prevent multiple inclusion #define _foo_h_ // This section is used only internally to foo.c #ifdef _foo_c_ . . . #endif // Public interface continues to end of file. #endif // _foo_h_ // Last-ish line in foo.h