预编译头

预编译头（precompiled header）是程序设计时把头文件编译为中间格式（如目标文件），以节约在开发过程中编译器反复编译该头文件的开销。 C语言、C++语言、Objective C语言等都有类似的技术。

有的头文件包含了巨量的源代码（如著名的windows.h），或者使用模板编程时要生成巨大的头文件模板库（如与)。为减少编译时间，某些编译器允许把头文件编译为某种中间形式称为预编译头（precompiled header），后续再编译源文件时就可以尽量直接使用这些预编译头。

简单示例
C++文件source.cpp包括header.hpp：

//header.hpp
...
//source.cpp
#include "header.hpp"
...

首次编译source.cpp时，编译器生成header.pch的预编译头。以后再编译该程序时，编译器会比较该头文件的时间戳，如果头文件没有改变，编译器直接使用预编译头。 

 Visual Studio 
Microsoft Visual Studio采取一个头文件（默认命名为stdafx.h），其内容是整个软件项目中被各个编译单元所使用的源代码，并极少修改。每个编译单元的源代码从最开始之处直至#include "stdafx.h"的内容完全相同。其中一个编译单元（源文件默认命名为"stdafx.cpp"）以#include "stdafx.h"为最后一行（不考虑程序注释），并用编译选项/Yc"stdafx.h"编译，这将生成预编译头（默认命名为"<项目名>.pch"）,包含了到#include "stdafx.h"这一行为止的所有代码编译结果。 

项目中的其他编译单元，使用/Yu"stdafx.h"编译，这将把从开头直至#include "stdafx.h"这一行的源代码跳过编译过程，直接用预编译头（默认命名为"<项目名>.pch"）代替。

stdafx中的AFX代表Application Framework eXtensions。AFX是Microsoft Foundation Classes (MFC)的旧称。编程者也可以用其他名字的头文件代替stdafx.h。

Visual Studio编译器确定预编译头的文件名，依照下述次序：
编译器选项/Fp中给出的参数；
源文件中预处理器指令#pragma hdrstop [( "filename" )]给出的预编译头的文件名。源文件从开始之处至该行#pragma hdrstop的内容，用于创建预编译头（若使用/Yc选项）或者被预编译头取代（若使用/Yu选项）。
/Yu"头文件基名.h"中的文件的base name加上后缀.PCH； 
当前源文件的的base name加上后缀.PCH。

 GCC 
GCC3.4版开始支持预编译头。当编译器输入文件为头文件时（例如sample.h），产生预编译头，其文件名称是原来的头文件追加".gch"后缀（如sample.h.gch）。当gcc编译源文件时，对于遇到的每个头文件，都在INCLUDE路径中的每个目录下，先搜寻追加".gch"后缀的预编译头，如果没有再搜索该头文件。如果找到预编译头且满足一些条件，则直接使用该预编译头；否则就把该头文件在源文件中展开并编译之。  可以在编译源文件时使用-H选项，如果显示的预编译头的全路径这一行是以"!"开始，说明编译器成功使用了预编译头；如果这一行以"x"开始，说明编译器找到了这一预编译头但未能使用它。

gcc对C语言的头文件与C++的头文件产生的格式不同，不能混用。如果头文件的名字不是".h"或".hpp"后缀结尾（C++标准库的头文件基本如此），可以用编译选项-x c++-header让gcc把当前的输入文件当作C++头文件来处理。用编译选项-x c-header让gcc把当前的输入文件当作C头文件来处理。

使用预编译头时必须满足下列条件：
编译一个源文件，至多只能使用一个预编译头。
编译源文件时，如果先于预编译头对应的头文件编译器就扫描到C/C++的词（token），则不能使用该预编译头。也就是说，在预编译头对应的头文件之前的源文件中，只能有预处理指令（preprocessor directive)。 不能在另一个头文件中引入使用预编译头。
预编译头与源文件必须是相同语言。不能在编译C++源文件时使用C语言的预编译头。 
预编译头与源文件应当由同一个编译器编译。以保证二进制兼容。 
在预编译头之前定义的宏（macro）或者被引入源程序时与预编译头生产时完全一致，或者预编译头没有用到该宏。这包括了由编译选项-D定义的宏、#define定义的宏、以及某些编译选项如-O或-Wdeprecated隐式定义的宏。
源文件用-g选项编译以获取调试信息时，其使用的预编译头也应该是用-g编译选项生成的。但是，用-g选项编译生成的预编译头，也可用在没有调试信息输出的源文件编译。 
生成与使用预编译头，必须使用相同的-m选项。该编译选项给出目标硬件平台的相关信息。 
生成与使用预编译头，必须使用相同的-fexceptions选项。
生成与使用预编译头，必须使用以-f, -p, -O开头的相同的命令行选项。目前还不知道哪些编译选项可以安全地改变或者决不能改变，最安全的选择是生成与使用预编译头使用相同的编译选项。已知下述选项可以安全地改变：
  -fmessage-length=  -fpreprocessed  -fsched-interblock -fsched-spec  
  -fsched-spec-load  -fsched-spec-load-dangerous 
  -fsched-verbose=number  -fschedule-insns  -fvisibility= 
  -pedantic-errors
上述条件如果不被满足，编译器将自动使用头文件而不是对应的预编译头。

 C++Builder 
在项目默认配置中，C++Builder编译器无保留地生成在#pragma hdrstop之前所有头文件的预编译头。如果可能的话，预编译头会被项目的所有模块共享。例如，当使用Visual Component Library时，通常首先会包含vcl.h头文件，该头文件含有大多数经常被用到的VCL头文件。因此，预编译头被全项目共享可以显著减少生成时间。

另外，C++Builder能设置为使用一个特定的头文件作为预编译头，和Visual C++提供的机制类似。

C++Builder 2009引入了“Precompiled Header Wizard（预编译头向导）”来为被包含的头文件提取所有源码，然后自动为特定的文件分类（例如排除那些不是项目成员或者没有Include防范的头文件）、生成并测试预编译头。

 外部链接 
 The Care and Feeding of Pre-Compiled Headers 
 Precompiled Headers with GCC
 Fractal eXtreme: Precompiled headers 
参考文献

Category:C语言
Category:C++