如何在C中构造#includes

时间:2008-11-09 19:58:30

标签: c include header rules-of-thumb

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

E.g。文件foo.c包含foo.h,其中包含foo.c的所有声明; bar.cbar.h相同。 foo1()内的函数foo.c调用bar1(),该bar.hbar.c中声明并在bar.h中定义。现在的问题是,我应该在foo.h内或foo.c内包含{{1}}吗?

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

6 个答案:

答案 0 :(得分:6)

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

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

答案 1 :(得分:4)

正如其他人所说,标题foo.h应该声明能够使用源文件foo.c提供的功能所必需的信息。这将包括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的功能,那么foo.h应该确保包含bar.h(通过包含它)。但是,如果bar.h的服务仅在foo.c中使用,并且使用foo.h的人不需要,则foo.h不应包含bar.h。

答案 2 :(得分:3)

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

答案 3 :(得分:2)

我在.h文件中包含了最小的标头集,并将其余标头包含在.c文件中。这有利于减少编译时间。根据您的示例,如果foo.h确实不需要bar.h但是仍然包含它,而其他一些文件包含foo.h,那么如果bar.h更改,则会重新编译该文件,即使它实际上可能不需要或使用bar.h

答案 4 :(得分:2)

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

如果file1的接口使用file2的接口,那么#include file2.h在file1.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文件的情况,那么这表明模块化已经崩溃,您应该考虑重组一些事情。

答案 5 :(得分:2)

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

  • 请记住foo.h的目的是促进foo.c的使用,因此请尽可能简单,有条理,不言自明。请放心使用 以及 时使用foo.c的功能 - 以及何时来使用它们。< / p>

  • foo.h声明foo.c的公共功能:函数,宏,typedef和( shudder )全局变量。

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

  • 如果foo.c需要额外的标题才能编译,请将其添加到foo.c

  • 如果要编译foo.h需要外部标头,请将其包含在foo.h

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

  • 如果由于某种原因需要外部标头才能让其他.c文件使用foo.c中的功能,请在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