编译器每次编译主程序时都会编译所有包含的头文件吗

Does compiler compiles all included header files along with the main program every time we compile that program?

本文关键字:编译 包含 文件 主程序 编译器      更新时间:2023-10-16

根据维基百科,这就是C预处理器所做的:

"预处理器用文件'stdio.h'的文本替换行#include <stdio.h>,其中声明了printf()函数等。">

如果这是真的,那么一个包含更多头文件的程序将需要更多的时间来编译?

因此,如果这是真的,那么一个包含更多标头的程序文件的编译会花费更多时间吗?

当然。严格地说,编译器需要查看的代码越多,处理它所需的时间就越多。对于一些真正大的项目来说,查看所有文件所需要的时间很容易成为一个问题。对于非常大和/或复杂的模板代码尤其如此,因为实际原因,这些代码必须位于头文件中。头文件本身的组织也会对编译时间产生影响。

然而,它并不像你想象的那么简单。它高度依赖于编译器的实现质量(QOI),而现代编译器实际上在大多数情况下都非常擅长处理头文件。

例如,GCC特别认可包含保护以减少处理时间。如今的编译器在处理复杂的模板代码方面做得越来越好,例如大多数标准库。根据我的经验,在VC++编译器上,包括windows.h(它几乎为整个Windows API提供了函数原型)并没有明显增加编译时间。如果其他一切都失败了,那么即使不是所有编译器,也有许多编译器都可以使用"预编译头"功能。

基本上,在它成为问题之前不要担心它。如果拥有更多的头文件有助于更好地组织代码,那么无论如何都要毫不犹豫地使用它们。

通常是的,“包含更多头文件的程序将需要更多的时间来编译。";。


不幸的是,标头的预处理内容可能会根据定义的宏符号和方式而有所不同。尤其是Microsoft的标头通常被设计为,以根据这些符号产生不同的结果(在标准C++中,它主要只是NDEBUG符号,这会影响assert的扩展)。因此,编译器是保守的,并对每个翻译单元进行一次又一次的预处理和头编译。

避免这种情况的一种常见技术是所谓的预编译头

据我所知,一种不常见的技术,可能没有被任何编译器实现,是允许程序员说"对于这批编译,你可以假设所有的头都将扩展到相同的",甚至可以将其作为默认(即使在假设不成立的情况下,可以产生与标准C++不同的结果)。

我非常喜欢后一种方法,因为它可以加快大多数构建的速度,但预编译的头文件是我们在实践中所拥有的。


David Vandevoorde为C++编写了一个模块提案。

带模块的语言示例:Modula-2、Ada、UCSD Pascal。

不幸的是,它还没有为C++11做好准备,但也许我们稍后会得到模块。

理论上是的-代码越多,编译时间越长。您包含的所有内容都是通过预处理器放置的,而不是您已经提到的include语句。

实际上,这取决于你包含了什么以及如何包含。如果你以适当的方式加入,对速度的影响可以忽略不计。包含过多的标准标头会造成名称空间污染,然后会产生编译时问题。标准库的整个代码已经编译完毕,将只进行链接。在Qt中,您可以单独包含每个类(例如#include <QWidget>),也可以包含模块中的所有类(例如,#include <QtGui>),该模块包含数千个标头。所以我从来没有注意到两种方法之间有相当大的编译时间差异。

一些想法和事实形成了我的经验:

  • 在计算机中提到了收割台防护装置。它们主要不是用于编译时优化,而是用于防止循环包含。所以如果在10个位置包含一个标头,那么它将只被解析一次。

  • 在C++中,只有从中的类派生时才包含在头中头球否则使用正向声明。

  • 使用私有实现习惯用法-这样您的头将只包含一个/几个指针。

  • 不要包含你的代码头,这是你不需要的c/cpp文件。一个好的构建系统分析标头对include语句的基础。您的代码不必要每次必须包含的任何标头发生更改时都会重新编译。

  • 除了编译时间外,链接时间也可能相当长。制作当然,该应用程序由DLL(DLL/so文件)组成,因此您可以避免在每次编译时将所有内容链接在一起。在一个项目中,我经历了大约10分钟的连接时间。客户拒绝使用DLL是因为一些深奥的原因。

  • 如果编译器时间真的是一个问题,那么值得在构建系统的引擎盖。一些不平凡的项目必须运行编译后/预编译脚本。检查这些脚本的执行情况只有在必要时才执行。

  • 在一个项目中,整个构建时间减少了在对制造工具进行一些修改后4小时至40分钟。如果你在Linux上,请与strace核实make是如何工作的。你会的令人惊讶的是,它进行了如此多不必要的文件访问。