6

假设我有一个 C 程序,它被分解为一组 *.c 和 *.h 文件。如果一个文件中的代码使用另一个文件中的函数,我应该在哪里包含头文件?在使用该函数的 *.c 文件中,还是在该文件的标头中?

例如文件foo.c包括foo.h,其中包含所有声明foo.cbar.c和相同bar.h。函数foo1()内部foo.c调用bar1(),在中声明bar.h和定义bar.c。现在的问题是,我应该包括bar.hinsidefoo.h还是 inside foo.c

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

4

6 回答 6

7

您应该在 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。这可能会导致链接问题,也是设计不良的标志
于 2008-11-09T20:10:50.730 回答
5

正如其他人所指出的,头文件 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 中定义。现在的问题是,我应该将 bar.h 包含在 foo.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

于 2008-11-09T22:32:02.363 回答
3

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

于 2008-11-09T20:03:28.000 回答
3

使用以下示例,foo.cfoo.h发现这些指南很有帮助:

  • 请记住, 的目的foo.h是为了方便使用foo.c,因此请尽可能保持其简单、有条理和不言自明。对解释如何以及何时foo.c使用- 以及何时使用它们的功能的注释要自由。

  • foo.h声明了以下公共特性foo.c:函数、宏、typedef 和(颤抖)全局变量。

  • 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
于 2008-11-09T22:21:55.380 回答
2

我在文件中包含尽可能少的标题集.h,并将其余部分包含在.c文件中。这有时会减少编译时间。鉴于您的示例,如果foo.h确实不需要bar.h但无论如何都包含它,并且其他一些文件包含foo.h,那么如果bar.h更改该文件将被重新编译,即使它实际上可能不需要或使用bar.h.

于 2008-11-09T20:02:55.130 回答
2

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

如果file1的接口使用file2的接口那么#include file2.h in file1.h

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

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

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

于 2008-11-09T20:27:51.477 回答