真正理解預編譯標頭檔案: Procompiled header

bamboogz99發表於2009-02-12

<!-- /* Font Definitions */ @font-face {font-family:"MS Mincho"; panose-1:2 2 6 9 4 2 5 8 3 4; mso-font-alt:"MS 明朝"; mso-font-charset:128; mso-generic-font-family:modern; mso-font-pitch:fixed; mso-font-signature:-1610612033 1757936891 16 0 131231 0;} @font-face {font-family:宋體; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"/@宋體"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"/@MS Mincho"; panose-1:2 2 6 9 4 2 5 8 3 4; mso-font-charset:128; mso-generic-font-family:modern; mso-font-pitch:fixed; mso-font-signature:-1610612033 1757936891 16 0 131231 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0in; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋體; mso-font-kerning:1.0pt;} p {mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; font-size:12.0pt; font-family:宋體; mso-bidi-font-family:宋體;} span.vcer {mso-style-name:vcer;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:8.5in 11.0in; margin:1.0in 1.25in 1.0in 1.25in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.Section1 {page:Section1;} /* List Definitions */ @list l0 {mso-list-id:834877154; mso-list-type:hybrid; mso-list-template-ids:1148638160 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l0:level1 {mso-level-tab-stop:21.0pt; mso-level-number-position:left; margin-left:21.0pt; text-indent:-21.0pt;} ol {margin-bottom:0in;} ul {margin-bottom:0in;} --> 經常碰到編譯錯誤:fatal error C1083: Cannot open precompiled header file: ****, No such file or directory,特別是學生經常會問到。而我只能根據自己的經驗告訴他們應該改一下VC裡面的一個設定:

precompiled header setting

這一解決方案在大多數情況下是可行的,因為學生建立的都是console工程,基本上用不到系統生成的stdafx.h檔案。但是,今天碰到的一個問題,才讓我真正明白pch(預編譯標頭檔案)的作用及意義。

開啟一個好久不用的工程paperguru,重新編譯一下,竟然顯示:

fatal error C1083: Cannot open precompiled header …

本想按照常規處理,將系統的use precompiled header file改為Not using precompiled headers。但是一想不對,我的許多*.cpp檔案都引用了stdafx.h中的標頭檔案,因此是行不通的。但是我還是做了一下驗證,果然提示找不到stdafx.h。在網上搜尋一番,終於徹底弄明白了pch檔案的用途。對於許多大的工程,系統中有些原始檔會經常包含一系列相同的標頭檔案,這時為了方便引用,更是為了編譯的效率,我們會將這多個標頭檔案全部include到一個*.h檔案中。VC預設的工程中都有一個stdafx.h,就是這樣一個*.h檔案。同時,也要生成一個stdafx.c/cpp檔案,該檔案什麼也不做,只是#include stdafx.h。這樣,系統在編譯stdafx.cpp時,將會自動找到其包含的標頭檔案。那麼其它原始檔只需要#include stdafx.h就可以了,而不用#include每一個標頭檔案。但是有兩點設定要注意,這也是產生上面錯誤的原因:

1.         stdafx.cppprecompiled header必須設定為create precompiled header file(.pch),意思我們需要根據stdafx.cpp來生成*.pch,通常是工程名.pch

pch for stdafx.cpp

 

2.         而其它需要呼叫precompiled headers檔案的原始檔的precompiled headers必須設定為use precompiled header file (.pch),如下圖:

pch for other cpp files

換句話說,一個(stdafx.cpp)是生產者,其它的是消費者。

產生上面編譯出錯的原因可能是無意中將所有的*.cpp檔案的該屬性都設定成了use precompiled header file(.pch),因此係統自然就找不到*.pch。這一錯誤經常出現,因為在其它工程中可能修改了該設定,但開啟新的工程時仍然使用前一工程的預設設定。

 

---------相關知識(轉載)----------

預編譯頭的概念:

所謂的預編譯頭就是把一個工程中的那一部分程式碼,預先編譯好放在一個檔案裡(通常是以.pch為副檔名的),這個檔案就稱為預編譯標頭檔案這些預先編譯好的程式碼可以是任何的 C/C++程式碼--------甚至是inline的函式,但是必須是穩定的,在工程開發的過程中不會被經常改變。如果這些程式碼被修改,則需要重新編譯生成預編譯標頭檔案。注意生成預編譯標頭檔案是很耗時間的。同時你得注意預編譯標頭檔案通常很大,通常有6-7M大。注意及時清理那些沒有用的預編譯標頭檔案。

也許你會問:現在的編譯器都有Time stamp的功能,編譯器在編譯整個工程的時候,它只會編譯那些經過修改的檔案,而不會去編譯那些從上次編譯過,到現在沒有被修改過的檔案。那麼為什麼還要預編譯標頭檔案呢?答案在這裡,我們知道編譯器是以檔案為單位編譯的,一個檔案經過修改後,會重新編譯整個檔案,當然在這個檔案裡包含的所有標頭檔案中的東西(.eg Macro, Preprocesser )都要重新處理一遍。VC的預編譯標頭檔案儲存的正是這部分資訊。以避免每次都要重新處理這些標頭檔案。

預編譯頭的作用:

根據上文介紹,預編譯標頭檔案的作用當然就是提高便宜速度了,有了它你沒有必要每次都編譯那些不需要經常改變的程式碼。編譯效能當然就提高了。

預編譯頭的使用:

要使用預編譯頭,我們必須指定一個標頭檔案,這個標頭檔案包含我們不會經常改變的程式碼和其他的標頭檔案,然後我們用這個標頭檔案來生成一個預編譯標頭檔案(.pch檔案) 想必大家都知道 StdAfx.h這個檔案。很多人都認為這是VC提供的一個系統級別的,編譯器帶的一個標頭檔案。其實不是的,這個檔案可以是任何名字的。我們來考察一個典型的由AppWizard生成的MFC Dialog Based 程式的預編譯標頭檔案。(因為AppWizard會為我們指定好如何使用預編譯標頭檔案,預設的是StdAfx.h,這是VC起的名字)。我們會發現這個標頭檔案裡包含了以下的標頭檔案:

#include <afxwin.h> // MFC core and standard components

#include <afxext.h> // MFC extensions

#include <afxdisp.h> // MFC Automation classes

#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls

#include <afxcmn.h>

這些正是使用MFC的必須包含的標頭檔案,當然我們不太可能在我們的工程中修改這些標頭檔案的,所以說他們是穩定的。

那麼我們如何指定它來生成預編譯標頭檔案。我們知道一個標頭檔案是不能編譯的。所以我們還需要一個cpp檔案來生成.pch 檔案。這個檔案預設的就是StdAfx.cpp。在這個檔案裡只有一句程式碼就是:#include “Stdafx.h”。原因是理所當然的,我們僅僅是要它能夠編譯而已?也就是說,要的只是它的.cpp的副檔名。我們可以用/Yc編譯開關來指定StdAfx.cpp來生成一個.pch檔案,通過/Fp編譯開關來指定生成的pch檔案的名字。開啟project ->Setting->C/C++ 對話方塊。把Category指向Precompiled Header。在左邊的樹形檢視裡選擇整個工程

Project Options(右下角的那個白的地方)可以看到 /Fp “debug/PCH.pch”,這就是指定生成的.pch檔案的名字,預設的通常是 <工程名>.pch(我的示例工程名就是PCH)。

然後,在左邊的樹形檢視裡選擇StdAfx.cpp.//這時只能選一個cpp檔案!

這時原來的Project Option變成了 Source File Option(原來是工程,現在是一個檔案,當然變了)。在這裡我們可以看到 /Yc開關,/Yc的作用就是指定這個檔案來建立一個Pch檔案。/Yc後面的檔名是那個包含了穩定程式碼的標頭檔案,一個工程裡只能有一個檔案的可以有YC開關。VC就根據這個選項把 StdAfx.cpp編譯成一個Obj檔案和一個PCH檔案。

然後我們再選擇一個其它的檔案來看看,//其他cpp檔案

在這裡,Precomplier 選擇了 Use ⋯⋯⋯一項,標頭檔案是我們指定建立PCH 檔案的stdafx.h 檔案。事實上,這裡是使用工程裡的設定,(如圖1/Yu”stdafx.h”

這樣,我們就設定好了預編譯標頭檔案。也就是說,我們可以使用預編譯頭功能了。以 下是注意事項:

1):如果使用了/Yu,就是說使用了預編譯,我們在每個.cpp檔案的最開頭,我強調一遍是最開頭,包含你指定產生pch檔案的.h檔案(預設是stdafx.h)不然就會有問題。如果你沒有包含這個檔案,就告訴你Unexpected file end. 如果你不是在最開頭包含的,你自己試以下就知道了,絕對有很驚人的效果..

fatal error C1010: unexpected end of file while looking for precompiled <!-- /* Font Definitions */ @font-face {font-family:宋體; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"/@宋體"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0in; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋體; mso-font-kerning:1.0pt;} p {mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; font-size:12.0pt; font-family:宋體; mso-bidi-font-family:宋體;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:8.5in 11.0in; margin:1.0in 1.25in 1.0in 1.25in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.Section1 {page:Section1;} -->

header directive

Generating Code...

2)如果你把pch檔案不小心丟了,編譯的時候就會產生很多的不正常的行為。根據以上 的分析,你只要讓編譯器生成一個pch檔案。也就是說把 stdafx.cpp(即指定/Yc的那個 cpp檔案)從新編譯一遍。當然你可以傻傻的 Rebuild All簡單一點就是選擇那個cpp檔案,按一下Ctrl + F7就可以了。不然可是很浪費時間的哦。

相關文章