介紹
在C和C ++程式語言中,頭檔案是檔案可以由C預處理器通過在源檔案中使用預處理器指令自動包含在另一個源檔案中的檔案。
頭檔案有時可能包含非常大量的原始碼(例如,分別在Microsoft Windows和OS X上的頭檔案windows.h和Cocoa / Cocoa.h)。特別是在大量使用模板的大型“頭檔案”庫(如特徵數學庫和Boost C ++庫)的出現時尤其如此。它們幾乎完全是作為用戶#includes的頭檔案編寫的,而不是在運行時連結。因此,每次用戶編譯他們的程式時,用戶基本上也重新編譯了許多頭庫。(這些將被預編譯到非“標題”庫中的共享對象或動態程式庫中。)
為了減少編譯時間,一些編譯器允許將頭檔案編譯成更快的編譯器處理形式。這箇中間表單被稱為預編譯頭檔案,通常保存在一個名為.pch擴展名或類似檔案的檔案中,例如GNU編譯器集合下的.gch。
用法
例如,給定一個包含header.hpp的C ++檔案source.cpp:
//source.cpp#include "header.hpp"...
第一次編譯source.cpp時,編譯器會生成一個預編譯頭header.pch。 下一次,如果此標頭的時間戳沒有改變,編譯器可以跳過與header.hpp相關的編譯階段,而是直接使用header.pch。
常見的實現
Microsoft Visual C和C ++
Microsoft Visual C ++(版本6.0和更新[需要的引證])可以預編譯任何代碼,而不僅僅是頭檔案。它可以通過兩種方式實現:預編譯所有代碼,直到名稱與/ Ycfilename選項匹配的檔案或(當/ Yc指定為不帶任何檔案名稱時)預編譯所有代碼,直到代碼中第一次出現#pragma hdrstop 預編譯的輸出保存在一個檔案中,該檔案的名稱以給定給/ Yc選項的檔案名稱,擴展名為.pch或者根據/ Fpfilename選項提供的名稱命名的檔案中。 / Yu選項,如果與/ Yc選項一起使用,會使編譯器使用來自此檔案的已編譯好的代碼。
stdafx.h是由Microsoft Visual Studio IDE嚮導生成的檔案,它描述了經常使用但幾乎不會更改的標準系統和項目特定包含檔案。
兼容的編譯器將預編譯此檔案以減少總體編譯時間。除非編譯選項/Yu'stdafx.h未選中(默認情況下),否則Visual C ++不會編譯源檔案中的#include“stdafx.h”之前的任何內容。它假定源中包含該行的所有代碼都已編譯。
stdafx.h中的afx代表應用程式框架擴展。 AFX是Microsoft基礎類(MFC)的原始縮寫。雖然名稱stdafx.h是默認使用的,但項目可以指定一個替代名稱。
clang
clang編譯器有兩種機制。
原始的,簡單的和不太強大的機制是pretokenized標頭,其中一個或多個源檔案中的辭彙標記流存儲在有效的標記快取中,從而可以在後續彙編中更快地檢索它們,而不是執行詞法分析再次在原始源檔案。
與完整的預編譯頭部機制相比,這具有語言獨立性的優勢,因為詞法分析在C,C ++,Objective C和Objective C ++語言的鏗鏘聲中是相同的,並且與架構無關,因為可以使用相同的令牌流在編譯不同的目標架構時。然而它的缺點是沒有進行簡單的詞法分析,需要對每個彙編執行令牌流的句法和語義分析;以及用預先編寫的檔案的詞法標記的大小線性地進行縮放編譯的時間,這對於完全成熟的預編譯機制不一定是這種情況。
預清除機制包括幾個輔助預處理器的小機制:快取檔案存在和日期戳記信息,以及記錄包含警告,以便可以快速跳過守護的代碼。
因此後來的clang開發引入了完全成熟的預編譯頭部機制。這既標記輸入原始碼,也執行語法和語義分析,將編譯器的內部生成的抽象語法樹(AST)和符號表寫入預編譯的頭檔案中。
與pretokenized頭檔案機制相比,這種擴展性好得多,因為在編譯器中讀取預編譯頭檔案時,不受輸入是線性讀取檔案流的影響(使用詞法標記),使用順序I / O. [5] AST被寫入預編譯頭檔案,編譯器可以使用隨機訪問I / O來讀取它,特別是不讀取預編譯AST中後面的代碼實際上沒有引用的部分,這是常見的頭檔案提供大型模板庫。這消除了讀取快取檔案與預編譯輸入大小線性縮放的問題。
然而,不利的一面是,相比於pretokenization機制,普遍性的損失。預編譯頭檔案必須存儲關於正在使用的語言方言的信息,直到是否在非C ++語言,目標體系結構,編譯器版本(更具體地說是編譯器正在使用的內部AST數據結構)以及預定義的預處理器宏列表;所以在重新讀取預編譯的頭檔案時,編譯器可以確保它使用了一個對編譯有效的預編譯頭。
clang的預編譯頭部方案,並且具有一些改進,例如一個預編譯頭部引用另一個內部使用的預編譯頭部的能力,也構成了其模組機制的基礎。它使用LLVM採用的相同位碼檔案格式,封裝在通用對象檔案格式或可擴展連結格式檔案中的特定於文本的段中。
GCC
預編譯頭檔案在GCC(3.4及更高版本)中受支持。 GCC的方法類似於VC和兼容編譯器。 GCC使用“.gch”後綴保存預編譯頭檔案的版本。編譯源檔案時,編譯器會檢查此檔案是否存在於同一目錄中,並儘可能使用它.
GCC只能使用預編譯版本,如果設定相同的編譯器開關,則編譯頭檔案時最多可使用一個。此外,只有預處理指令可以放在預編譯頭之前(因為在任何可編譯的代碼之前它必須直接或間接地通過另一個正常頭包含)。
GCC通過擴展自動識別大多數頭檔案。但是,如果失敗(例如由於非標準擴展頭),可以使用-x開關確保GCC將檔案視為標頭。
C ++ Builder
在預設項目配置中,C ++ Builder編譯器隱式地為源模組包含的所有頭生成預編譯頭檔案,直到找到#pragma hdrstop行。如果可能的話,預編譯頭檔案將為項目的所有模組共享。例如,使用Visual Component Library時,通常首先包含vcl.h頭檔案,其中包含大多數常用的VCL頭檔案。因此,可以在所有項目模組之間共享預編譯頭檔案,這大大縮短了編譯時間。
另外,可以將C ++ Builder作為預編譯頭檔案使用特定的頭檔案,類似於Visual C ++提供的機制。
C ++ Builder 2009引入了一個“預編譯頭檔案嚮導”,它解析項目的所有源模組以包含頭檔案,對它們進行分類(即,如果頭檔案是項目的一部分或不包含頭檔案,則不包括頭檔案),並生成自動測試指定檔案的預編譯頭。