當您在 Visual Studio 中建立新專案時,名為 pch.h 的先行編譯標頭檔 會新增至專案。 (Visual Studio 2017 和更早版本中,檔案稱為 stdafx.h .) 檔案的目的是加速建置程式。 任何穩定的標頭檔,例如 標準程式庫標頭, <vector> 都應該包含在這裡。 只有在先行編譯標頭或其包含的任何檔案都修改時,才會編譯先行編譯標頭。 如果您只在專案原始程式碼中進行變更,建置將會略過先行編譯標頭的編譯。

先行編譯標頭的編譯器選項為 /Y 。 在專案屬性頁中,選項位於 [ 組態屬性 > C/C++ > 先行編譯標頭 ] 之下。 您可以選擇不使用先行編譯的標頭,也可以指定標頭檔名和輸出檔的名稱和路徑。

自訂先行編譯器代碼

對於需要大量時間來建置的大型專案,您可以考慮建立自訂先行編譯的檔案。 Microsoft C 和 C++ 編譯器提供對任何 C 或 C++ 程式碼進行先行編譯的選項,包括內嵌程式碼。 使用此效能功能,您可以編譯穩定的程式碼主體、將程式碼的編譯狀態儲存在檔案中,並在後續編譯期間,將先行編譯的程式碼與仍在開發中的程式碼結合。 每個稍後的編譯速度較快,因為穩定程式碼不需要重新編譯。

何時先行編譯原始程式碼

先行編譯的程式碼在開發週期期間很有用,以減少編譯時間,特別是在:

  • 您一律會使用不常變更的大型程式碼主體。

  • 您的套裝程式含多個模組,所有模組都使用一組標準 Include 檔案和相同的編譯選項。 在此情況下,所有 Include 檔案都可以先行編譯成一個先行編譯的標頭。 如需處理包含檔案之較新方式的詳細資訊,請參閱 比較標頭單位、模組和先行編譯標頭

    第一個編譯 (建立先行編譯標頭檔) 比後續編譯還要長一點。 後續的編譯可以藉由包含先行編譯的程式碼,更快速地繼續進行。

    您可以先行編譯 C 和 C++ 程式。 在 C++ 程式設計中,常見的做法是將類別介面資訊分成標頭檔。 這些標頭檔稍後可以包含在使用 類別的程式中。 藉由先行編譯這些標頭,您可以減少程式編譯所需的時間。

    雖然每個來源檔案只能使用一個先行編譯標頭檔 () .pch 檔案,但您可以在專案中使用多個 .pch 檔案。

    預先編譯器代碼的兩個選項

    您可以先行編譯任何 C 或 C++ 程式碼;您不限於先行編譯標頭檔。

    先行編譯需要規劃,但如果您預先編譯原始程式碼,而不是簡單的標頭檔,它會提供更快速的編譯。

    當您知道原始程式檔使用一組通用標頭檔,或想要在先行編譯中包含原始程式碼時,先行編譯器代碼。

    先行編譯標頭選項 ( /Yc 建立先行編譯標頭檔) ( /Yu 使用先行編譯標頭檔) 。 用來 /Yc 建立先行編譯的標頭。 搭配選用 hdrstop pragma 使用時, /Yc 可讓您先行編譯標頭檔與原始程式碼。 選取 /Yu 以在現有的編譯中使用現有的先行編譯標頭。 您也可以搭配 /Yc /Yu 選項使用 /Fp ,以提供先行編譯標頭的替代名稱。

    的編譯器選項參考文章 /Yu ,並 /Yc 討論如何在開發環境中存取此功能。

    先行編譯標頭一致性規則

    因為 PCH 檔案包含電腦環境的相關資訊,以及程式的相關記憶體位址資訊,所以您應該只在建立程式的電腦上使用 PCH 檔案。

    每個檔案使用先行編譯標頭的一致性規則

    編譯 /Yu 程式選項可讓您指定要使用的 PCH 檔案。

    當您使用 PCH 檔案時,除非另有指定,否則編譯器會假設在您建立 PCH 檔案時生效的相同編譯環境。 編譯環境包含編譯器選項、pragmas 等等。 如果編譯器偵測到不一致,它會發出警告,並盡可能識別不一致。 這類警告不一定表示 PCH 檔案發生問題;它們只會警告您可能發生的衝突。 下列各節將說明 PCH 檔案的一致性需求。

    編譯器選項一致性

    使用 PCH 檔案時,下列編譯器選項可能會觸發不一致的警告:

  • 使用預處理器 () /D 選項所建立的宏,在建立 PCH 檔案和目前編譯的編譯之間必須相同。 未檢查已定義的常數狀態,但如果這些宏變更,可能會發生無法預測的結果。

  • PCH 檔案不適用於 /E /EP 選項。

  • 必須先使用 [產生流覽資訊]) (選項或 [排除區域變數 /Fr ( /FR ) ] 選項來建立 PCH 檔案,然後使用 PCH 檔案的後續編譯可以使用這些選項。

    C 7.0 相容 ( /Z7 )

    如果此選項在建立 PCH 檔案時生效,則稍後使用 PCH 檔案的編譯可以使用偵錯資訊。

    如果建立 PCH 檔案時,C 7.0 相容 ( /Z7 ) 選項不會生效,則稍後會使用 PCH 檔案編譯並 /Z7 觸發警告。 偵錯資訊會放在目前的 .obj 檔案中,而 PCH 檔案中定義的本機符號則不適用於偵錯工具。

    包含路徑一致性

    PCH 檔案不包含標頭包含建立時生效之標頭包含路徑的相關資訊。 當您使用 PCH 檔案時,編譯器一律會使用目前編譯中指定的標頭 include 路徑。

    來源檔案一致性

    當您指定 [使用先行編譯標頭檔 () /Yu ] 選項時,編譯器會忽略所有預處理器指示詞, (包括出現在原始程式碼中且將會預先編譯的 pragmas) 。 這類預處理器指示詞指定的編譯必須與用於建立先行編譯標頭檔 () /Yc 選項的編譯相同。

    Pragma 一致性

    在建立 PCH 檔案期間處理的 Pragmas 通常會影響稍後使用 PCH 檔案的檔案。 comment message pragmas 不會影響編譯的其餘部分。

    這些 pragmas 只會影響 PCH 檔案內的程式碼;它們不會影響稍後使用 PCH 檔案的程式碼:

    comment
    linesize

    message

    pagesize

    subtitle
    title

    這些 pragmas 會保留為先行編譯標頭的一部分,並影響使用先行編譯標頭之編譯的其餘部分:

    alloc_text
    auto_inline
    check_stack
    code_seg
    data_seg

    function
    include_alias
    init_seg
    inline_depth

    inline_recursion
    intrinsic
    optimize

    pointers_to_members
    setlocale
    vtordisp
    warning

    /Yc 和 /Yu 的一致性規則

    當您使用 使用 /Yc /Yu 建立的先行編譯標頭時,編譯器會將目前的編譯環境與建立 PCH 檔案時存在的編譯環境進行比較。 請務必使用一致的編譯器選項、pragmas 等) ,指定與前一個 (一致的環境。 如果編譯器偵測到不一致,它會發出警告,並盡可能識別不一致。 這類警告不一定表示 PCH 檔案發生問題;它們只會警告您可能發生的衝突。 下列各節說明先行編譯標頭的一致性需求。

    編譯器選項一致性

    下表列出使用先行編譯標頭時,可能會觸發不一致警告的編譯器選項:

    /Fr /FR 產生Microsoft來源瀏覽器資訊 若要讓 /Fr /FR 選項具有 選項有效 /Yu ,它們也必須在建立先行編譯標頭時生效。 使用先行編譯標頭的後續編譯也會產生來源瀏覽器資訊。 瀏覽器資訊會放在單 .sbr 一檔案中,並以與 CodeView 資訊相同的方式由其他檔案參考。 您無法覆寫來源瀏覽器資訊的位置。 /GA /GD /GE /Gw /GW Windows 通訊協定選項 在建立先行編譯標頭和目前編譯的編譯之間必須相同。 如果這些選項不同,編譯器就會發出警告。 產生完整的偵錯資訊 如果建立先行編譯標頭時,這個選項生效,則使用先行編譯的後續編譯可以使用該偵錯資訊。 如果在 /Zi 建立先行編譯標頭時沒有作用,則後續使用先行編譯的編譯和 /Zi 選項會觸發警告。 偵錯資訊會放在目前的物件檔中,而且偵錯工具無法使用先行編譯標頭中定義的本機符號。

    在專案中使用先行編譯標頭

    上一節提供先行編譯標頭的概觀:/Yc 和 /Yu、/Fp 選項和 hdrstop pragma。 本節描述在專案中使用手動先行編譯標頭選項的方法;其結尾為範例 makefile 及其所管理的程式碼。

    如需在專案中使用手動先行編譯標頭選項的另一種方法,請研究位於 Visual Studio 預設設定期間所建立之目錄中的其中一個 makefiles MFC\SRC 。 這些 Makefiles 採用與本節中所呈現的類似方法。 它們使用Microsoft程式維護公用程式 (NMAKE) 宏,並提供更大的建置程式控制權。

    建置程式中的 PCH 檔案

    軟體專案的程式碼基底通常包含在多個 C 或 C++ 原始程式檔、物件檔、程式庫和標頭檔中。 一般而言,makefile 會將這些專案的組合協調成可執行檔。 下圖顯示使用先行編譯標頭檔之 makefile 的結構。 此圖表中的 NMAKE 宏名稱和檔案名與 PCH 範例 makefile 中找到的範例程式碼一致,以及 PCH 的範例程式碼

    此圖使用三個圖表裝置來顯示建置程式的流程。 具名矩形代表每個檔案或宏;這三個宏代表一或多個檔案。 陰影區域代表每個編譯或連結動作。 箭號會顯示編譯或連結程式期間合併的檔案和宏。

    使用先行編譯標頭檔之 makefile 的結構

    從圖表頂端開始,和 BOUNDRY 都是 NMAKE 宏, STABLEHDRS 您可以在其中列出不需要重新編譯的檔案。 這些檔案是由命令字串編譯

    CL /c /W3 /Yc$(BOUNDRY) applib.cpp myapp.cpp

    只有在先行編譯標頭檔 () STABLE.pch 不存在,或是您對兩個宏中列出的檔案進行變更時。 不論是哪一種情況,先行編譯標頭檔只會包含宏中所列檔案的程式 STABLEHDRS 代碼。 列出您想要先行編譯宏的最後 BOUNDRY 一個檔案。

    這些宏中所列的檔案可以是標頭檔或 C 或 C++ 原始程式檔。 (單一 PCH 檔案不能與 C 和 C++ sources.) 您可以使用 hdrstop 宏,在檔案內的 BOUNDRY 某個時間點停止先行編譯。 如需詳細資訊,請參閱 hdrstop

    下圖的接下來, APPLIB.obj 代表您最終應用程式中所使用的支援程式碼。 它是從 APPLIB.cpp 建立的、宏中列出的檔案,以及先行編譯標頭檔中 UNSTABLEHDRS 的先行編譯器代碼。

    MYAPP.obj 代表您的最終應用程式。 它是從 MYAPP.cpp 建立的、宏中列出的檔案,以及先行編譯標頭檔中 UNSTABLEHDRS 的先行編譯器代碼。

    最後,藉由連結宏 APPLIB.obj (和 MYAPP.obj ) 中列出的 OBJS 檔案,建立可執行檔 MYAPP.EXE () 。

    PCH 的範例 makefile

    下列 makefile 使用宏和 !IF !ELSE !ENDIF 控制流程命令結構,以簡化專案的調整。

    # Makefile : Illustrates the effective use of precompiled
    #            headers in a project
    # Usage:     NMAKE option
    # option:    DEBUG=[0|1]
    #            (DEBUG not defined is equivalent to DEBUG=0)
    OBJS = myapp.obj applib.obj
    # List all stable header files in the STABLEHDRS macro.
    STABLEHDRS = stable.h another.h
    # List the final header file to be precompiled here:
    BOUNDRY = stable.h
    # List header files under development here:
    UNSTABLEHDRS = unstable.h
    # List all compiler options common to both debug and final
    # versions of your code here:
    CLFLAGS = /c /W3
    # List all linker options common to both debug and final
    # versions of your code here:
    LINKFLAGS = /nologo
    !IF "$(DEBUG)" == "1"
    CLFLAGS   = /D_DEBUG $(CLFLAGS) /Od /Zi
    LINKFLAGS = $(LINKFLAGS) /COD
    LIBS      = slibce
    !ELSE
    CLFLAGS   = $(CLFLAGS) /Oselg /Gs
    LINKFLAGS = $(LINKFLAGS)
    LIBS      = slibce
    !ENDIF
    myapp.exe: $(OBJS)
        link $(LINKFLAGS) @<<
    $(OBJS), myapp, NUL, $(LIBS), NUL;
    # Compile myapp
    myapp.obj  : myapp.cpp $(UNSTABLEHDRS)  stable.pch
        $(CPP) $(CLFLAGS) /Yu$(BOUNDRY)    myapp.cpp
    # Compile applib
    applib.obj : applib.cpp $(UNSTABLEHDRS) stable.pch
        $(CPP) $(CLFLAGS) /Yu$(BOUNDRY)    applib.cpp
    # Compile headers
    stable.pch : $(STABLEHDRS)
        $(CPP) $(CLFLAGS) /Yc$(BOUNDRY)    applib.cpp myapp.cpp
    

    STABLEHDRS除了建置程式中 PCH 檔案中「使用先行編譯標頭檔之 Makefile 的結構」圖中顯示的 、 BOUNDRYUNSTABLEHDRS 宏之外,這個 Makefile 還提供 CLFLAGS 宏和 LINKFLAGS 宏。 您必須使用這些宏來列出編譯器和連結器選項,這些選項適用于您建置偵錯或最終版本的應用程式可執行檔。 另外還有一個宏, LIBS 您可以在其中列出專案所需的程式庫。

    makefile 也會使用 !IF!ELSE!ENDIF 來偵測您是否在 DEBUG NMAKE 命令列上定義符號:

    NMAKE DEBUG=[1|0]
    

    這項功能可讓您在開發期間使用相同的 makefile,以及程式的最終版本。 DEBUG=0用於最終版本。 下列命令列是相等的:

    NMAKE
    NMAKE DEBUG=0
    

    如需 makefiles 的詳細資訊,請參閱 NMAKE 參考。 另請參閱 MSVC 編譯器選項MSVC 連結器選項

    PCH 的範例程式碼

    下列原始程式檔用於 PCH 檔案中所述的 makefile 和 PCH 的範例 makefile。 批註包含重要資訊。

    原始程式檔 ANOTHER.H

    // ANOTHER.H : Contains the interface to code that is not
    //             likely to change.
    #ifndef __ANOTHER_H
    #define __ANOTHER_H
    #include<iostream>
    void savemoretime( void );
    #endif // __ANOTHER_H
    

    原始程式檔 STABLE.H

    // STABLE.H : Contains the interface to code that is not likely
    //            to change. List code that is likely to change
    //            in the makefile's STABLEHDRS macro.
    #ifndef __STABLE_H
    #define __STABLE_H
    #include<iostream>
    void savetime( void );
    #endif // __STABLE_H
    

    原始程式檔 UNSTABLE.H

    // UNSTABLE.H : Contains the interface to code that is
    //              likely to change. As the code in a header
    //              file becomes stable, remove the header file
    //              from the makefile's UNSTABLEHDR macro and list
    //              it in the STABLEHDRS macro.
    #ifndef __UNSTABLE_H
    #define __UNSTABLE_H
    #include<iostream>
    void notstable( void );
    #endif // __UNSTABLE_H
    

    原始程式檔 APPLIB.CPP

    // APPLIB.CPP : This file contains the code that implements
    //              the interface code declared in the header
    //              files STABLE.H, ANOTHER.H, and UNSTABLE.H.
    #include"another.h"
    #include"stable.h"
    #include"unstable.h"
    using namespace std;
    // The following code represents code that is deemed stable and
    // not likely to change. The associated interface code is
    // precompiled. In this example, the header files STABLE.H and
    // ANOTHER.H are precompiled.
    void savetime( void )
        { cout << "Why recompile stable code?\n"; }
    void savemoretime( void )
        { cout << "Why, indeed?\n\n"; }
    // The following code represents code that is still under
    // development. The associated header file is not precompiled.
    void notstable( void )
        { cout << "Unstable code requires"
                << " frequent recompilation.\n";
    

    原始程式檔 MYAPP.CPP

    // MYAPP.CPP : Sample application
    //             All precompiled code other than the file listed
    //             in the makefile's BOUNDRY macro (stable.h in
    //             this example) must be included before the file
    //             listed in the BOUNDRY macro. Unstable code must
    //             be included after the precompiled code.
    #include"another.h"
    #include"stable.h"
    #include"unstable.h"
    int main( void )
        savetime();
        savemoretime();
        notstable();
    

    比較標頭單位、模組和先行編譯標頭
    C/C++ 建置參考
    MSVC 編譯器選項C++ 中的模組概觀
    教學課程:使用模組匯入 C++ 標準程式庫
    逐步解說:在 Visual C++ 專案中建置和匯入標頭單位
    逐步解說:將 STL 程式庫匯入為標頭單位

  •