有时我看到有人编译这样的 C 程序:
gcc -o hello hello.c hello.h
据我所知,我们只需要将头文件放入 C 程序中,例如:
#include "somefile"
并编译 C 程序:gcc -o hello hello.c
.
我们什么时候需要编译头文件或者为什么?
首先,一般来说:
如果这些.h
文件确实是典型的 C 风格的头文件(而不是完全不同的东西,只是碰巧用.h
扩展名命名),那么不,没有理由独立“编译”这些头文件。头文件旨在包含在实现文件中,而不是作为独立的翻译单元提供给编译器。
由于典型的头文件通常只包含可以在每个翻译单元中安全重复的声明,因此完全可以预期“编译”头文件不会产生有害后果。但与此同时,它不会实现任何有用的东西。
基本上,编译hello.h
为一个独立的翻译单元相当于创建一个dummy.c
仅包含#include "hello.h"
指令的退化文件,并将该dummy.c
文件提供给编译器。它会编译,但没有任何意义。
其次,专门针对 GCC:
许多编译器会根据文件扩展名对文件进行不同的处理。.h
当带有扩展名的文件作为命令行参数提供给编译器时,GCC 会对其进行特殊处理。GCC 没有将其视为常规翻译单元,而是为该文件创建一个预编译的头.h
文件。
你可以在这里阅读:http: //gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html
因此,这就是您可能会看到.h
文件被直接馈送到 GCC 的原因。
好的,让我们了解主动代码和被动代码之间的区别。
活动代码是功能、过程、方法的实现,即应该编译为可执行机器代码的代码片段。我们将它存储在 .c 文件中,并确定我们需要编译它。
被动代码本身并没有被执行,但它需要解释不同的模块如何相互通信。通常,.h 文件只包含原型(函数头)、结构。
一个例外是宏,它在形式上可以包含一个活动部分,但您应该了解它们是在构建(预处理)的早期阶段使用的,只是简单的替换。在编译时,宏已经被替换为您的 .c 文件。
另一个例外是 C++ 模板,它应该在 .h 文件中实现。但是这里有一个类似于宏的故事:它们在早期阶段被替换(实例化)并且形式上,彼此实例化是另一种类型。
总之,我认为,如果模块形成正确,我们永远不应该编译头文件。
在某些系统中,试图加速完全解析的“.c”文件的组装将包含文件的预组装称为“编译头文件”。然而,它是一种优化技术,对于实际的 C 开发来说并不是必需的。
这种技术基本上计算了包含语句并保留了扁平包含的缓存。通常,C 工具链将递归地剪切和粘贴包含的文件,然后将整个项目传递给编译器。使用预编译的标头缓存,工具链将检查是否有任何输入(定义、标头等)已更改。如果没有,那么它将向编译器提供已经扁平化的文本文件片段。
此类系统旨在加快开发速度;然而,许多这样的系统非常脆弱。随着计算机的加速和源代码管理技术的改变,在公共项目中实际使用的标头预编译器越来越少。
在您真正需要编译优化之前,我强烈建议您避免预编译头文件。
当我们像这样包含头文件时:#include <header.h> 或 #include "header.h" 然后您的预处理器将其作为输入并将整个文件包含在源代码中。预处理器用指定文件的内容替换#include 指令。您可以通过 GCC 的 -E 标志来检查这一点,它会生成 .i(信息文件)临时文件,或者可以使用专门的 cpp(LINUX) 模块,该模块在我们执行 GCC 时由编译器驱动程序自动使用。所以它实际上会与你的源代码一起编译,不需要编译它。
我认为我们确实需要预处理(也许不调用编译)头文件。因为据我了解,在编译阶段,头文件应该包含在c文件中。例如,在 test.h 我们有
typedef enum{
a,
b,
c
}test_t
在 test.c 我们有
void foo()
{
test_t test;
...
}
在编译过程中,我认为编译器会将头文件和c文件中的代码放在一起,头文件中的代码将被预处理并替换c文件中的代码。同时,我们最好在makefile中定义包含路径。
您不需要编译头文件。它实际上并没有做任何事情,因此尝试运行它是没有意义的。但是,这是检查拼写错误和错误的好方法,因此以后会更容易。