例如,文件foo.c包括foo.h,其中包含了所有foo.c的声明;bar.c和bar.h也是如此。foo.c中的函数foo1()调用bar1(),后者在bar.h中声明并在bar.c中定义。现在的问题是,我应该在foo.h中包含bar.h,还是在foo.c中包含bar.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()调用bar.h中声明并在bar.c中定义的函数bar1()。现在问题是,我应该将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。
使用 foo.c
和 foo.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
我在.h
文件中包含最小的头文件集,并在.c
文件中包含其余部分。这样做有时可以减少编译时间。以你的例子为例,如果foo.h
并不真正需要bar.h
但仍然包含它,而某个其他文件包含foo.h
,那么如果bar.h
发生更改,即使它实际上并不需要或使用bar.h
,该文件也将被重新编译。
如果file1的接口使用file2的接口,则在file1.h中#include file2.h。
如果file1.c中的实现使用file2.c中的内容,则file1.c应该#include file2.h。
我必须承认,因为我总是在file1.c中#include file1.h,所以如果file2.h已经在file1.h中#include了,我通常不会费心在file1.c中直接#include file2.h。
如果您发现两个.c文件互相#include彼此的.h文件,则说明模块化已经崩溃,您应该考虑重新构建一下。