预编译头文件的实际工作方式

时间:2016-04-08 12:15:36

标签: c++ visual-c++ precompiled-headers

我的所有查询都与vc ++编译器有关,但我猜其他c ++编译器也有相同的行为。

  1. 预编译头文件是否与预处理程序相关,或者这完全与编译过程有关?或两者?我有几个猜测:
    • PCH引擎仅扩展MACRO定义和嵌套标头,并将它们转换为二进制格式(pch文件)。在这种情况下,所有源文件(我的意思是cpp / hpp也可能包含在PCH中)将在项目中的每个源文件中重新编译。或者不是?
    • 所有源文件只会被编译一次并被拉入单个obj文件中?例如,在此示例中将编译变体库多少次?即只有一次 - 在PCH中或两次 - 不在PCH中但在两个* .cpp文件中或三次 - 在PCH和两个* .cpp文件中?为什么?
  2. //stdafx.h
    #include <boost/variant/variant.hpp>
    
    //test1.cpp
    #include "stdafx.h"
    #include <boost/variant/variant.hpp>
    ...
    
    //test2.cpp
    #include "stdafx.h"
    ...
    
    1. 我应该在预编译的标头中放入哪些文件?我想这是在项目中随处可见的东西,很少变化。那么图书馆呢?我们只在几个源文件中使用boost,我们应该把它放在PCH中吗?

3 个答案:

答案 0 :(得分:2)

我对VC ++的内脏一无所知。但是,对编译器设计和理论有一定的了解,这些所谓的“预编译头”不仅仅是经典编译器设计的初始词法分析和标记化阶段的结果。

考虑一个包含以下内容的简单头文件:

#ifdef FOO
#define BAR 10
#else
#undef FOOBAR
class Foo {
public:
     void bar();
};
#include "foobar.h"
#endif

您必须了解使用所谓的“预编译”标头的效果必须与使用标头文件相同。

在这里,你真的不知道这个头文件会做什么。这完全取决于实际包含头文件时定义的预处理器宏。您不知道此头文件将定义哪些宏。您不知道此头文件将取消定义哪些宏。您不知道此头文件将包含哪些其他头文件。你真的不太了解这里。

在概念上,“预编译”头文件的唯一方法就是预先解析它。将语言的各个元素,单个关键字(如“#ifdef”,“class”和所有其他关键字)转换为单独的二进制标记。删除任何评论,空白等...

编译传统语言的第一阶段涉及将纯文本源解析为内部语言元素。词法分析和标记化阶段。在解析各个语言元素之后,尝试弄清楚如何将得到的,已解析的源代码转换为对象模块。这就是99%的编译器工作的地方。最初的词法分析阶段并不是很多,但是你可以做的就是“预编译”源代码,并保存标记化源代码的内部二进制表示,这样就可以跳过这个阶段,当实际的代码是使用“预编译”源编译。

我假设VC ++对预编译头文件的内容几乎没有任何限制。但是,如果存在一些限制 - 比如说,预编译头文件除了经典防护之外不能有任何条件预处理器指令(ifdef / ifndef),那么就可以做更多工作来生成预编译头文件,并保存一个这里有更多的工作。对预编译头文件内容的其他限制也可能导致一些额外的功能转移到预编译阶段。

答案 1 :(得分:1)

由于 stdafx.cpp 而编译的预编译头文件是stdafx.h。开发人员会很少更改,并且经常需要此标头中的头文件和符号。例如Windows.hvector以及一些全局宏和符号。经常使用,我指的是给定项目中的所有文件。

此类文件(PCH)的目的和帮助是什么?好吧,VC ++编译器将以递归方式编译整个stdafx.h文件,所有头文件都包含所有宏和其他符号。这是第一次,它将花费大量时间,并将生成PCH文件(因此 p 重新 c ompiled h eader)。在后续构建中,通过stdafx.h包含的元素将不会被重新编译(因为它们已经是某种二进制/预编译格式)。这减少了构建时间,并且会根据stdafx.h文件中放置的元素(标题,符号等)的数量而有所不同。

如果你有一个庞大的代码库,而且stdafx中的元素较少,你将无法获得优势(例如,包括常见的Windows和STL标题,到处都有externs和typedef)。最好找到这些元素,并将它们放入stdafx.h,然后从标题/ CPP文件中删除它们。这将大大减少总体构建时间。

您可以在此处进行更改: enter image description here

答案 2 :(得分:0)

我认为 MSVC 会为翻译单元的唯一预编译标头寻找 <application_name>.pch,并使用它代替预处理 #line 1 "c:\\application_name\\stdafx.h" 文件中 .i 下包含的转置标头,如果它可用。预编译头可能是序列化的 AST,即头已被词法分析并解析为 AST 表示。然后它不需要对预处理输出的这个区域进行词法(或解析),而只使用 .pch,它包含在 stdafx.h 下预处理器输出中写入的词法+解析输出。预处理器已经完成了 stafx.h 上的所有其他工作,例如扩展宏(不会出现在 .i 文件/预处理器输出中)。