6

在我的编程生涯中,我参与过许多 C 项目,头文件结构通常属于以下两种模式之一:

  1. 一个包含所有函数原型的头文件
  2. .h每个文件一个.c文件,仅包含该模块中定义的函数的原型。

选项 2 的优点对我来说是显而易见的——它使在多个项目之间共享模块变得更便宜,并使模块之间的依赖关系更容易看到。

但是选项 1 的优点是什么?它必须有一些优点,否则它不会那么受欢迎。


这个问题适用于 C++ 和 C,但我从未在 C++ 项目中看到过 #1。

#defines、 s 等的位置struct也各不相同,但对于这个问题,我想关注函数原型。

4

9 回答 9

8

我认为#1的主要动机是......懒惰。人们认为管理依赖关系太难了,将事物拆分成单独的文件会使事情变得更加明显,和/或认为将所有内容都分开文件有点“矫枉过正”。

当然,这也可能是“历史原因”的一种情况,即程序或项目是从小东西发展而来的,没有人花时间重构头文件。

于 2009-06-04T12:57:20.293 回答
5

选项 1 允许将所有定义放在一个位置,这样您就必须只包含/搜索一个文件,而不必包含/搜索许多文件。如果您的系统作为库交付给第三方,这个优势会更加明显——他们不太关心您的库结构,他们只是希望能够使用它。

于 2009-06-04T12:55:57.270 回答
4
  • 1只是不必要的。我看不出这样做的充分理由,而且有很多可以避免的。

遵循#2的三个规则并且没有问题:

  • 每个头文件都以

    #ifndef _HEADER_Namefile
    #define _HEADER_Namefile_
    

结束文件

    #endif

这将允许您在同一个模块上多次包含同一个头文件(可能会无意中发生)而不会引起任何大惊小怪。

  • 你不能在你的头文件上有定义......这是每个人都认为他/她知道的关于函数原型的事情,但几乎永远忽略了全局变量。如果您想要一个全局变量,根据定义,它应该在定义 C 模块之外可见,请使用 extern 关键字:

    extern unsigned long G_BEER_COUNTER;
    

它指示编译器 G_BEER_COUNTER 符号实际上是无符号长整数(因此,像声明一样工作),在其他一些模块上将具有正确的定义/初始化。(这也允许链接器保留已解析/未解析的符号表。)实际定义(没有 extern 的相同语句)位于模块 .c 文件中。

  • 只有在被证明绝对必要的情况下,您才会在头文件中包含其他头文件。include 语句应该只在 .c 文件(模块)上可见。这使您可以更好地解释依赖关系,并找到/解决问题。
于 2009-06-04T14:19:11.847 回答
4

为每个 .c 使用不同的 .h 的另一个原因是编译时间。如果只有一个 .h(或者如果有更多但您将它们全部包含在每个 .c 文件中),则每次更改 .h 文件时,都必须重新编译每个 .c 文件。在大型项目中,这可能会浪费大量宝贵的时间,这也可能会破坏您的工作流程。

于 2009-06-04T14:44:35.663 回答
4

我会推荐一种混合方法:为程序的每个组件制作一个单独的标题,可以想象它可以独立使用,然后制作一个包含所有这些组件的项目标题。这样,每个源文件只需要包含一个标头(如果重构组件,则无需更新所有源文件),但您可以保持声明的逻辑组织,并使重用代码变得容易。

于 2010-08-07T14:14:51.643 回答
3

还有我相信第三种选择:每个.c都有自己的.h,但也有一个.h包含所有其他.h文件。这以保持更新为代价带来了两全其美.h,尽管这可以自动完成。

使用此选项,您可以在内部使用单个.h文件,但第 3 方可以只包含包罗万象的.h文件。

于 2009-06-04T13:08:46.433 回答
1

当您有一个包含成百上千个小头文件的非常大的项目时,依赖项检查和编译会显着减慢,因为必须打开和读取大量小文件。这个问题通常可以通过使用预编译的头文件来解决。

于 2009-06-04T13:02:12.173 回答
0

在 C++ 中,您肯定希望每个类有一个头文件,并使用上面提到的预编译头文件。

整个项目的一个头文件是不可行的,除非项目非常小——比如学校作业

于 2009-06-04T13:08:24.203 回答
0

这取决于一个头文件/源文件中有多少功能。如果您需要包含 10 个文件只是为了对某些内容进行排序,那就不好了。

例如,如果我想使用 STL 向量,我只包含在内,我不在乎使用向量需要哪些内部结构。GCC 包括 8 个其他头文件——allocator、algobase、construct、uninitialized、vector 和 bvector。仅仅为了使用向量而包含所有这 8 个是很痛苦的,你同意吗?

但是库内部标题应该尽可能稀疏。如果不包含不必要的东西,编译器会更开心。

于 2009-06-04T14:47:42.117 回答