預編譯頭
沒有或很少條目連入本條目。 (2015年6月18日) |
預編譯頭(precompiled header)是程序設計時把頭文件編譯為中間格式(如目標文件),以節約在開發過程中編譯器反覆編譯該頭文件的開銷。 C語言、C++語言、Objective C語言等都有類似的技術。
有的頭文件包含了巨量的源代碼(如著名的windows.h
),或者使用模板編程時要生成巨大的頭文件模板庫(如Eigen math library與Boost C++ libraries)。為減少編譯時間,某些編譯器允許把頭文件編譯為某種中間形式稱為預編譯頭(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編譯器確定預編譯頭的文件名,依照下述次序:[1]
- 編譯器選項/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"後綴的預編譯頭,如果沒有再搜索該頭文件。如果找到預編譯頭且滿足一些條件,則直接使用該預編譯頭;否則就把該頭文件在源文件中展開並編譯之。[2] 可以在編譯源文件時使用-H選項,如果顯示的預編譯頭的全路徑這一行是以"!"開始,說明編譯器成功使用了預編譯頭;如果這一行以"x"開始,說明編譯器找到了這一預編譯頭但未能使用它。
gcc對C語言的頭文件與C++的頭文件產生的格式不同,不能混用。如果頭文件的名字不是".h"或".hpp"後綴結尾(C++標準庫的頭文件基本如此),可以用編譯選項-x c++-header讓gcc把當前的輸入文件當作C++頭文件來處理。用編譯選項-x c-header讓gcc把當前的輸入文件當作C頭文件來處理。[3]
使用預編譯頭時必須滿足下列條件:
- 編譯一個源文件,至多只能使用一個預編譯頭。
- 編譯源文件時,如果先於預編譯頭對應的頭文件編譯器就掃描到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 (頁面存檔備份,存於網際網路檔案館)
參考文獻
- ^ MSDN Help Document: hdrstop. [2015-06-07]. (原始內容存檔於2018-09-24).
- ^ GCC online documentation §3.20: Using Precompiled Headers. [2015-06-07]. (原始內容存檔於2015-09-18).
- ^ GCC online documentation §3.2 Options Controlling the Kind of Output. [2015-06-07]. (原始內容存檔於2015-09-18).