【问题标题】:Weird Behavior with gcc precompiled headersgcc 预编译头文件的奇怪行为
【发布时间】:2017-03-29 22:03:05
【问题描述】:

我在让预编译的头文件工作时遇到了麻烦,所以我想出了以下最小工作示例。

这是头文件foo.h

#include <iostream>
using namespace std;

void hello() {
    cout << "Hello World" << endl;
}

我将其编译为g++ -c foo.h,给了我一个编译后的标题foo.gch。我希望当我编译以下包含foo.h 的源文件时,它应该选择标题foo.h.gch,我很好。

// test.cpp
#include <cstdio>   // Swap ordering later
#include "foo.h"    // ------------------

int main() {
     hello();
}

但令人惊讶的是,这并没有使用foo.h.gch 编译,而是使用foo.h。要验证您可以将其编译为g++ -H test.cpp

但是,如果我将包含的头文件的顺序更改如下:

// test.cpp
#include "foo.h"    // ------------------
#include <cstdio>   // Ordering swapped

int main() {
     hello();
}

现在如果我使用g++ -H test.cpp 编译,它会从foo.h.gch 编译,哇!

所以我想知道这是否是 GCC 中的错误,还是我们应该使用这样的预编译头文件?无论哪种情况,我认为了解它很有用..

【问题讨论】:

    标签: c++ gcc precompiled-headers


    【解决方案1】:

    简而言之,预编译的头文件是这样工作的:

    当您请求创建“.pch”文件时,编译器会照常处理源文件。当它这样做时,它的内部结构(主要是名称表和所有相关数据)被填充。最后,它会对这些内部结构进行快照并将其保存到“.pch”文件中。

    稍后,当编译包含“.pch”文件的头文件的源文件时,编译器可以省略对头文件的昂贵处理,并从“.pch”加载准备使用的快照文件。

    显然,只有在以下情况下才能在不影响语义的情况下做到这一点:

    • 包含指令位于其他任何内容之前;
    • 编译器选项相同。

    包含指令之前的任何内容都可以:

    • 在编译器的内部数据结构中添加一些东西;
    • 影响头文件的处理;
    • 更改此处描述的实体之间的关系。

    因此,在这种情况下,加载内部数据结构的快照将是错误的,因为无法保证它会使这些结构保持与正常处理标头后相同的状态。

    【讨论】:

      【解决方案2】:

      在 GCC 中,预编译的标头在它们是 only 标头并且首先被包含(没有任何先前的标头)时才起作用。

      This answer 解释了为什么会这样。 另请参阅 GCC 文档的 Precompiled headers 章节,其中说:

      • 在特定编译中只能使用一个预编译头文件。
      • 一旦看到第一个 C 标记,就不能使用预编译的标头。

      顺便说一句,预编译一些大型头文件(尤其是在 C++ 中)可能不值得付出努力。 YMMV。

      【讨论】:

      • 就我而言,它似乎超级有效!我正在预编译 CGAL 头文件,它已将编译时间从大约 18 秒缩短到 2 秒。
      【解决方案3】:

      来自海湾合作委员会manual pages

      一旦看到第一个 C 标记,就不能使用预编译的标头。

      因此,在您的预编译标头中包含 &lt;cstdio&gt; 或首先包含它会起作用。

      【讨论】:

      • 感谢您的回答!当然,这是记录在案的:)。如果只有程序员(或者像我这样没有耐心的程序员)可以通读它们。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-08
      • 2010-09-08
      • 2012-06-13
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 2011-12-01
      相关资源
      最近更新 更多