預編譯和預處理



預編譯和預處理以及編譯選項的控制  


總是對這三個不是很清晰,今天回來後準備轉載,記在博客上。嘿嘿,我是不是很調皮啊偷笑


一 預編譯:

爲了增加編譯速度往往要提前對一些頭文件及代碼進行編譯,然後給後面正式編譯時使用,以節省開銷。這些文件代碼基本上不會更改,比如MFC的一些頭文件以及一些必要的API使用代碼,當然,你也可以把你自己的一部分代碼封裝起來到一個C或C++文件中,(比如在其中包含一些頭文件或必要的代碼什麼的,然後在VC-C/C++--PreCompiled Headers裏選擇第三項Create compiled Header file)來指定爲預編譯頭文件,這樣就在以後的程序修改中編譯時不會反覆編譯這部分。當然過多的使用預編譯頭文件會大大降低編譯的速度,所以可以使用下面的預處理指令:

#pragma hdrstop表示預編譯頭文件到此爲止,後面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能佔太多磁盤空間,所以使用這個選項排除一些頭文件。
有時單元之間有依賴關係,比如單元A依賴單元B,所以單元B要先於單元A編譯。你可以用#pragma startup指定編譯優先級,如果使用了#pragma package(smart_init) ,BCB就會根據優先級的大小先後編譯。

舉例:主要是轉別人的博文,感謝。

今天在改一個很大的程序,慢慢看,慢慢改。突然發現一個.c文件,裏面什麼也沒有,

就幾個頭文件,我一看,我靠,這不是把簡單的問題搞複雜了嗎,隨手刪掉那個c文件。

結果不能編譯了,我靠:

fatal error C1083: Cannot open precompiled header file: \'Debug/v13_3.pch\':

No such file or directory

怎麼rebuild all都不行。

上網查了一下,才搞懂了:

----------------總結------

如果工程很大,頭文件很多,而有幾個頭文件又是經常要用的,那麼

1。把這些頭文件全部寫到一個頭文件裏面去,比如寫到preh.h

2。寫一個preh.c,裏面只一句話:#include "preh.h"

3。對於preh.c,在project setting裏面設置creat precompiled headers,對於其他

.c文件,設置use precompiled header file

//

哈哈

我試了一下,效果很明顯,不用precompiled header,編譯一次我可以去上個廁所,用

precompiled header,編譯的時候,我可以站起來伸個懶腰,活動活動就差不多啦

---------轉載的文章----------

預編譯頭的概念:

所謂的預編譯頭就是把一個工程中的那一部分代碼,預先編譯好放在一個文件裏(通常是

以.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”。原因是理所當然的,我們僅僅是要它能

夠編譯而已?D?D?D也就是說,要的只是它的.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 文件的stda

fx.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

header directive

Generating Code...

2)如果你把pch文件不小心丟了,編譯的時候就會產生很多的不正常的行爲。根據以上

的分析,你只要讓編譯器生成一個pch文件。也就是說把 stdafx.cpp(即指定/Yc的那個

cpp文件)從新編譯一遍。當然你可以傻傻的 Rebuild All。簡單一點就是選擇那個cpp

文件,按一下Ctrl + F7就可以了。不然可是很浪費時間的哦。

二 預處理

預處理指令中#PRAGMA是用得最多的,最複雜。所以直接拷別人總結的:

pragma comment的使用
該宏放置一個註釋到對象文件或者可執行文件。

#pragma comment( comment-type [,"commentstring"] )comment-type是一個預定義的標識符,指定註釋的類型,應該是compiler,exestr,lib,linker之一。commentstring是一個提供爲comment-type提供附加信息的字符串,
Remarks:
1、compiler:放置編譯器的版本或者名字到一個對象文件,該選項是被linker忽略的。
2、exestr:在以後的版本將被取消。
3、lib:放置一個庫搜索記錄到對象文件中,這個類型應該是和commentstring(指定你要Liner搜索的lib的名稱和路徑)這個庫的名字放在Object文件的默認庫搜索記錄的後面,linker搜索這個這個庫就像你在命令行輸入這個命令一樣。你可以在一個源文件中設置多個庫記錄,它們在object文件中的順序和在源文件中的順序一樣。如果默認庫和附加庫的次序是需要區別的,使用Z編譯開關是防止默認庫放到object模塊。4、linker:指定一個連接選項,這樣就不用在命令行輸入或者在開發環境中設置了。只有下面的linker選項能被傳給Linker.
/DEFAULTLIB

/EXPORT

/INCLUDE

/MANIFESTDEPENDENCY

/MERGE

/SECTION

(1)/DEFAULTLIB:library/DEFAULTLIB 選項將一個 library 添加到 LINK 在解析引用時搜索的庫列表。用 /DEFAULTLIB
指定的庫在命令行上指定的庫之後和 .obj 文件中指定的默認庫之前被搜索。
忽略所有默認庫 (/NODEFAULTLIB) 選項重寫 /DEFAULTLIB:library。如果在兩者中指定了相同的 library 名稱,忽略庫 (/NODEFAULTLIB:library) 選項將重寫 /DEFAULTLIB:library。

(2)/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]


使用該選項,可以從程序導出函數,以便其他程序可以調用該函數。也可以導出數據。通常在 DLL 中定義導出。entryname 是調用程序要使用的函數或數據項的名稱。ordinal 在導出表中指定範圍在 1 至 65,535 的索引;如果沒有指定 ordinal,則 LINK 將分配一個。NONAME 關鍵字只將函數導出爲序號,沒有 entryname。

DATA 關鍵字指定導出項爲數據項。客戶程序中的數據項必須用 extern __declspec(dllimport) 來聲明。
有三種導出定義的方法,按照建議的使用順序依次爲:

源代碼中的 __declspec(dllexport)

.def 文件中的 EXPORTS 語句

LINK 命令中的 /EXPORT 規範

所有這三種方法可以用在同一個程序中。LINK 在生成包含導出的程序時還創建導入庫,除非生成中使用了 .exp 文件。
LINK 使用標識符的修飾形式。編譯器在創建 .obj 文件時修飾標識符。如果 entryname 以其未修飾的形式指定給鏈接器(與其在源代碼中一樣),則 LINK 將試圖匹配該名稱。如果無法找到唯一的匹配名稱,則 LINK 發出錯誤信息。當需要將標識符指定給鏈接器時,請使用 Dumpbin 工具獲取該標識符的修飾名形式。

(3)/INCLUDE:symbol
/INCLUDE 選項通知鏈接器將指定的符號添加到符號表。

若要指定多個符號,請在符號名稱之間鍵入逗號 (,)、分號 (;) 或空格。在命令行上,對每個符號指定一次 /INCLUDE:symbol。
鏈接器通過將包含符號定義的對象添加到程序來解析 symbol。該功能對於添包含不會鏈接到程序的庫對象非常有用。用該選項指定符號將通過 /OPT:REF 重寫該符號的移除。

我們經常用到的是#pragma   comment(lib,"*.lib")這類的。#pragma   comment(lib,"Ws2_32.lib")表示鏈接Ws2_32.lib這個庫。   和在工程設置裏寫上鍊入Ws2_32.lib的效果一樣,不過這種方法寫的   程序別人在使用你的代碼的時候就不用再設置工程settings了

 

 

 

 


#pragma C++
解析#pragma指令
在所有的預處理指令中,#Pragma 指令可能是最複雜的了,它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C++語言完全兼容的情況下,給出主機或操作系統專有的特徵。依據定義,編譯指示是機器或操作系統專有的,且對於每個編譯器都是不同的。
其格式一般爲: #Pragma Para
其中Para 爲參數,下面來看一些常用的參數。

(1)message 參數。 Message 參數是我最喜歡的一個參數,它能夠在編譯信息輸出窗
口中輸出相應的信息,這對於源代碼信息的控制是非常重要的。其使用方法爲:
#Pragma message(“消息文本”)
當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。
當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什麼地方定義了_X86這個宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
當我們定義了_X86這個宏以後,應用程序在編譯時就會在編譯輸出窗口裏顯示“_
X86 macro activated!”。我們就不會因爲不記得自己定義的一些特定的宏而抓耳撓腮了

(2)另一個使用得比較多的pragma參數是code_seg。格式如:
#pragma co
de_seg( ["section-name"[,"section-class"] ] )
它能夠設置程序中函數代碼存放的代碼段,當我們開發驅動程序的時候就會使用到它。

(3)#pragma once (比較常用)
只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到兼容性並沒有太多的使用它。

(4)#pragma hdrstop表示預編譯頭文件到此爲止,後面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能佔太多磁盤空間,所以使用這個選項排除一些頭文件。
有時單元之間有依賴關係,比如單元A依賴單元B,所以單元B要先於單元A編譯。你可以用#pragma startup指定編譯優先級,如果使用了#pragma package(smart_init) ,BCB就會根據優先級的大小先後編譯。

(5)#pragma resource "*.dfm"表示把*.dfm文件中的資源加入工程。*.dfm中包括窗體
外觀的定義。

(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等價於:
#pragma warning(disable:4507 34) // 不顯示4507和34號警告信息
#pragma warning(on
ce:4385) // 4385號警告信息僅報告一次
#pragma warning(error:164) // 把164號警告信息作爲一個錯誤。
同時這個pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這裏n代表一個警告等級(1---4)。
#pragma warning( push )保存所有警告信息的現有的警告狀態。
#pragma warning( push, n)保存所有警告信息的現有的警告狀態,並且把全局警告
等級設定爲n。
#pragma warning( pop )向棧中彈出最後一個警告信息,在入棧和出棧之間所作的
一切改動取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在這段代碼的最後,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
該指令將一個註釋記錄放入一個對象文件或可執行文件中。
常用的lib關鍵字,可以幫我們連入一個庫文件。

(8)#pragma pack()
我們知道在VC中,對於想結構體Struct這樣的類型,VC採用8字節對齊的方式,如果我們不想使用8字節對齊(在網絡變成中經常需要這樣),我們可以在結構體前面加上
#pragma pack(1)
struct
{
......
}
#pragma pack( )

以下是另一個轉載:

在vc6的時代頭文件一般使用ifndef define endif
在vc7的時代頭文件一般成了pragma on
ce
不知道有沒有人深究其中的意義
爲什麼有這樣的代碼,是爲了頭文件不被重複引用,那樣編譯器抱錯的,這兩種方法都是同樣的目的,有沒有區別呢?
還是舉例來說明,可能有好幾個庫,每個庫內部可能都有public.h這個文件,如果使用
ifndef public_h
define public_h
...
endif
那麼當一個文件同時引用兩個這樣的庫時,後一個庫裏的文件就不被編譯了,而pragma on
ce可以保證文件只被編譯一次
看起來pragma on
ce比ifndef define endif要好,那麼ifndef define endif
的地方都pragma on
ce好了。今天碰到了又一個例子,比如你有一個zlib.h在幾個庫都用到,而爲了方便,把zlib每個目錄下copy了一分,因爲這個文件不會作修改,已經很完整了,這個時候如果使用pragma once,就會重複定義,看來ifndef define endif還是又派上用場的地方。
所以對於公有或者接口的文件,使用ifndef define endif,對於內部的文件使用pragma on
ce.

#pragma once 與 #ifndef #define #endif 的區別

對於#pragma once,根據MSDN解說,能夠防止一個文件被多次包含。與#ifndef #define #endif形式的文件保護相比,前者是平臺相關的,可移植性比較差,但是它效率更高,因爲它不需要去打開包含的文件,就可以判斷這個文件有沒有被包含。當然這個工作是系統幫我們完成的。
後者的優點在於它是語言相關的特性,所以可移植性好。但是在包含一個文件的時候,只有打開這個文件,根據文件的保護宏是否已經被定義來判斷此文件是否已經被包含過。效率相對較低。當然在#i nclude的時候,程序員也可以自己判斷所要包含的文件的保護宏是否已經被定義,來決定是否要包含這個文件。類似下面的代碼:
#ifndef FILE_H_#i nclude "file.h"#endif這樣作可以得到較高的效率,而且保證可移植性。但是文件之間的依賴性較高,如果一個文件的保護宏改變的話,所有使用如上形式包含這個文件的文件都要修改。有悖於模塊化的思想。


#pragma da
ta_seg用法總結 (2008-09-05 12:54:54)
標籤:雜談   分類:編程

    Windows在一個Win32程序的地址空間周圍築了一道牆。通常,一個程序的地址空間中的數據是私有的,對別的程序而言是不可見的。但是執行STRPROG的多個執行實體表示了STRLIB在程序的所有執行實體之間共享數據是毫無問題的。當您在一個STRPROG窗口中增加或者刪除一個字符串時,這種改變將立即反映在其它的窗口中。

在全部例程之間,STRLIB共享兩個變量:一個字符數組和一個整數(記錄已儲存的有效字符串的個數)。STRLIB將這兩個變量儲存在共享的一個特殊內存區段中:

#pragma    data_seg ("shared")
int      iTotal = 0 ; 

WCHAR    szStrings [MAX_STRINGS][MAX_LENGTH + 1] = { '\0' } ;
#pragma       da
ta_seg ()       

第一個#pragma敘述建立數據段,這裏命名爲shared。您可以將這段命名爲任何一個您喜歡的名字。在這裏的#pragma敘述之後的所有初始化了的變量都放在shared數據段中。第二個#pragma敘述標示段的結束。對變量進行專門的初始化是很重要的,否則編譯器將把它們放在普通的未初始化數據段中而不是放在shared中。

連結器必須知道有一個「shared」共享數據段。在「Project Settings」對話框選擇「Link」頁面卷標。選中「STRLIB」時在「Project Options」字段(在Release和Debug設定中均可),包含下面的連結敘述:

/SECTION:shared,RWS

字母RWS表示段具有讀、寫和共享屬性。或者,您也可以直接用DLL原始碼指定連結選項,就像我們在STRLIB.C那樣:

#pragma comment(linker,"/SECTION:shared,RWS")
共享的內存段允許iTotal變量和szStrings字符串數組在STRLIB的所有例程之間共享。因爲MAX_STRINGS等於256,而MAX_LENGTH等於63,所以,共享內存段的長度爲32,772字節-iTotal變量需要4字節,256個指針中的每一個都需要128字節。

在Win16環境中,DLL的全局數據對每個載入它的進程來說都是相同的;而在Win32環境中,情況卻發生了變化,DLL函數中的代碼所創建的任何對象(包括變量)都歸調用它的線程或進程所有。當進程在載入DLL時操作系統自動把DLL地址映射到該進程的私有空間,也就是進程的虛擬地址空間,而且也複製該DLL的全局數據的一份拷貝到該進程空間。也就是說每個進程所擁有的相同的DLL的全局數據,它們的名稱相同,但其值卻並不一定是相同的,而且是互不干涉的。因此,在Win32環境下要想在多個進程中共享數據,就必須進行必要的設置。在訪問同一個Dll的各進程之間共
享存儲器是通過存儲器映射文件技術實現的。也可以把這些需要共享的數據分離出來,放置在一個獨立的數據段裏,並把該段的屬性設置爲共享。必須給這些變量賦初值,否則編譯器會把沒有賦初始值的變量放在一個叫未被初始化的數據段中。
#pragma da
ta_seg預處理指令用於設置共享數據段。例如:
#pragma da
ta_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma da
ta_seg()

 在#pragma data_seg("SharedDataName")和#pragma data_seg()之間的所有變量將被訪問該Dll的所有進程看到和共享。再加上一條指令

#pragma comment(linker,"/section:.SharedDataName,rws"),

那麼這個數據節中的數據可以在所有DLL的實例之間共享。所有對這些數據的操作都針對同一個實例的,而不是在每個進程的地址空間中都有一份。

   1.#pragma data_seg()一般用於DLL中。也就是說,在DLL中定義一個共享的,有名字的數據段。最關鍵的是:這個數據段中的全局變量可以被多個進程共享。否則多個進程之間無法共享DLL中的全局變量。

   2.共享數據必須初始化,否則微軟編譯器會把沒有初始化的數據放到.BSS段中,從而導致多個進程之間的共享行爲失敗。

   3.你所謂的結果正確是一種錯覺。如果你在一個DLL中這麼寫:

#pragma data_seg("MyData")

 int g_Value; // Note that the global is not initialized.

 

#pragma data_seg()

DLL提供兩個接口函數:

int GetValue()
{
     return g_Value;
}

void SetValue(int n)
{
     g_Value = n;
}

然後啓動兩個進程A和B,A和B都調用了這個DLL,假如A調用了SetValue(5); B接着調用int m = GetValue(); 那麼m的值不一定是5,而是一個未定義的值。因爲DLL中的全局數據對於每一個調用它的進程而言,是私有的,不能共享的。假如你對g_Value進行了初始化,那麼g_Value就一定會被放進MyData段中。換句話說,如果A調用了SetValue(5); B接着調用int m = GetValue(); 那麼m的值就一定是5!這就實現了跨進程之間的數據通信!


下面看一個實際應用,用共享數據來統計應用程序啓動的次數,並作相應的處理。

 

在應用程序的入口處:
//控制應用程序只能啓動一次
#pragma da
ta_seg("flag_data")
   int count=0;
#pragma da
ta_seg()
#pragma comment(linker,"/SECTION:flag_da
ta,RWS")

程序中:
   if(count>1)
     {
      MessageBox("已經啓動了一個應用程序","Warning",MB_OK);
      return FLASE;
}
   count++;

 


Visual C++ 6.0編譯指示收藏
新一篇: C++ 中的cast(顯式類型轉換) | 舊一篇: 看一個人是否快樂,不要看笑容
Document Source:

Pragma Directives, Preprocessor Reference, Visual C++ Programmer Guide.

 

每種C和C++的實現支持對其宿主機或操作系統唯一的功能。例如,一些程序需要精確控制超出數據所在的儲存空間,或着控制特定函數接受參數的方式。#pragma指示使每個編譯程序在保留C和C++語言的整體兼容性時提供不同機器和操作系統特定的功能。編譯指示被定義爲機器或操作系統特定的,並且通常每種編譯程序是不同的。

語法:

#pragma token_string

“token_string”是一系列字符用來給出所需的特定編譯程序指令和參數。數字符號“#”必須是包含編譯指令的行中第一個非空白字符;而空白字符可以隔開數字符號“#”和關鍵字“pragma”。在#pragma後面,寫任何翻譯程序能夠作爲預處理符號分析的文本。#pragma的參數類似於宏擴展。

如果編譯程序發現它不認得一個編譯指示,它將給出一個警告,可是編譯會繼續下去。

爲了提供新的預處理功能,或者爲編譯程序提供由實現定義的信息,編譯指示可以用在一個條件語句內。C和C++編譯程序可以識別下列編譯程序指令。

alloc_text
 comment
 init_seg*
 optimize
 
auto_inline
 component
 inline_depth
 pack
 
bss_seg
 da
ta_seg
 inline_recursion
 pointers_to_members*
 
check_stack
 function
 intrinsic
 setlocale
 
co
de_seg
 hdrstop
 message
 vtordisp*
 
const_seg
 include_alias
 on
ce
 warning
 

*僅用於C++編譯程序。

1  alloc_text
#pragma alloc_text( "textsection", function1, ... )

命名特別定義的函數駐留的代碼段。該編譯指示必須出現在函數說明符和函數定義之間。

alloc_text編譯指示不處理C++成員函數或重載函數。它僅能應用在以C連接方式說明的函數——就是說,函數是用extern "C"連接指示符說明的。如果你試圖將這個編譯指示應用於一個具有C++連接方式的函數時,將出現一個編譯程序錯誤。

由於不支持使用__based的函數地址,需要使用alloc_text編譯指示來指定段位置。由textsection指定的名字應該由雙引號括起來。

alloc_text編譯指示必須出現在任何需要指定的函數說明之後,以及這些函數的定義之前。

在alloc_text編譯指示中引用的函數必須和該編譯指示處於同一個模塊中。如果不這樣做,使以後一個未定義的函數被編譯到一個不同的代碼段時,錯誤會也可能不會被捕獲。即使程序一般會正常運行,但是函數不會分派到應該在的段。

alloc_text的其它限制如下:

它不能用在一個函數內部。

它必須用於函數說明以後,函數定義以前。

2  auto_inline
#pragma auto_inline( [{on | off}] )

當指定off時將任何一個可以被考慮爲作爲自動嵌入擴展候選的函數排除出該範圍。爲了使用auto_inline編譯指示,將其緊接着寫在一個函數定義之前或之後(不是在其內部)。該編譯指示將在其出現以後的第一個函數定義開始起作用。auto_inline編譯指示對顯式的inline函數不起作用。

3  bss_seg
#pragma da
ta_seg( ["section-name"[, "section-class"] ] )

爲未初始化數據指定缺省段。data_seg編譯指示除了工作於已初始化數據而不是未初始化的以外具有一樣的效果。在一些情況下,你能使用bss_seg將所有未初始化數據安排在一個段中來加速你的裝載時間。

#pragma bss_seg( "MY_DATA" )

將導致把#pragma語句之後的未初始化的數據安排在一個叫做MY_DATA的段中。

用bss_seg編譯指示分配的數據不包含任何關於其位置的信息。

第二個參數section-class是用於兼容2.0版本以前的Visual C++的,現在將忽略它。

4  check_stack
#pragma check_stack([ {on | off}] )

#pragma check_stack{+ | –}

如果指定off(或者“-”)指示編譯程序關閉堆棧探測,或者指定on(或“+”)打開堆棧探測。如果沒有給出參數,堆棧探測將根據默認設置決定。該編譯指示將在出現該指示之後的第一個函數開始生效。堆棧探測既不是宏和能夠生成嵌入代碼函數的一部分。

如果你沒有給出check-_stack編譯指示的參數,堆棧檢查將恢復到在命令行指定的行爲。詳細情況見編譯程序參考。#pragma check_stack和/Gs選項的互相作用情況在表2.1中說明。

表 2.1 使用check_stack編譯指示

編譯指示
 用/Gs選項編譯?
 行爲
 
#pragma check_stack()或#pragma check_stack
 是
 後續的函數關閉堆棧檢查
 
#pragma check_stack()或#pragma check_stack
 否
 後續的函數打開堆棧檢查
 
#pragma check_stack(on)或#pragma check_stack(+)
 是或者否
 後續的函數打開堆棧檢查
 
#pragma check_stack(off)或#pragma check_stack(-)
 是或者否
 後續的函數關閉堆棧檢查
 

5  code_seg
#pragma co
de_seg( ["section-name"[,"section-class"] ] )

指定分配函數的代碼段。code_seg編譯指示爲函數指定默認的段。你也能夠像段名一樣指定一個可選的類名。使用沒有段名字符串的#pragma code_seg將恢復分配到編譯開始時候的狀態。

6  const_seg
#pragma const_seg( ["section-name"[, "section-class"] ] )

指定用於常量數據的默認段。data_seg編譯指示除了可以工作於所有數據以外具有一樣的效果。你能夠使用該編譯指示將你的常量數據保存在一個只讀的段中。

#pragma const_seg( "MY_DATA" )

導致在#pragma語句後面的常量數據分配在一個叫做MY_DATA的段中。

用const_seg編譯指示分配的數據不包含任何關於其位置的信息。

第二個參數section-class是用於兼容2.0版本以前的Visual C++的,現在將忽略它。

7  comment
#pragma comment( comment-type [, commentstring] )

將描述記錄安排到目標文件或可執行文件中去。comment-type是下面說明的五個預定義標識符中的一個,用來指定描述記錄的類型。可選的commentstring是一個字符串文字值用於爲一些描述類型提供附加的信息。因爲commentstring是一個字符串文字值,所以它遵從字符串文字值的所有規則,例如換碼字符、嵌入的引號(")和聯接。

7-1
 
compiler
在目標文件中放置編譯程序名和版本號。該描述記錄被連接程序忽略。如果你爲這個記錄類型提供一個commentstring參數,編譯程序將生成一個警告。

7-2
 
exestr
將commentstring放置到目標文件中去。在連結時,這個字符串再被放到可執行文件去中。當可執行文件被裝載時這個字符串不會被裝入內存,然而,它可以被一個能夠在文件中搜索可打印字符串的程序找到。該描述記錄的一個用處是在可執行文件中嵌入版本號或者類似的信息。

7-3
 
lib
將一個庫搜索記錄放置到目標文件中去。該描述類型必須有包含你要連接程序搜索的庫名(和可能的路徑)的commentstring參數。因爲在目標文件中該庫名先於默認的庫搜索記錄,所以連接程序將如同你在命令行輸入這些庫一樣來搜索它們。你可以在一個源文件中放置多個庫搜索記錄,每個記錄將按照它們出現在源文件中的順序出現在目標文件中。

7-4
 
linker
在目標文件中放置連接程序選項。你可以用這個描述類型指定連接程序選項來代替在Project Setting對話框中Link頁內的選項。例如,你可以指定/include選項以強迫包含一個符號:

#pragma comment(linker, "/include:__mySymbol")

7-5
 
user
在目標文件中包含一個普通描述記錄。commentstring參數包含描述的文本。該描述記錄將被連接程序忽略。

 

下面的編譯指示導致連接程序在連接時搜索EMAPI.LIB庫。連接程序首先在當前工作目錄然後在LIB環境變量指定的路徑中搜索。

#pragma comment( lib, "emapi" )

下面的編譯指示導致編譯程序將其名字和版本號放置到目標文件中去。

The following pragma causes the compiler to place the name and version number of the compiler in the object file:

#pragma comment( compiler )

注意,對於具有commentstring參數的描述記錄,你可以使用其它用作字符串文字量的宏來提供宏擴展爲字符串文字量。你也能夠聯結任何字符串文字量和宏的組合來擴展成爲一個字符串文字量。例如,下面的語句是可以接受的:

#pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ )

8  component
#pragma component( browser, { on | off }[, references [, name ]] )

#pragma component( minrebuild, on | off )

 

從源文件內控制瀏覽信息和依賴信息的收集。

8-1
 
瀏覽信息(
Browser

你可以將收集打開或關閉,你也可以指定收集時忽略特別的名字。

使用on或off在編譯指示以後控制瀏覽信息的收集。例如:

#pragma component(browser, off)

終止編譯程序收集瀏覽信息。

注意,爲了用這個編譯指示打開瀏覽信息的收集,必須先從Project Setting對話框或者命令行允許瀏覽信息。

references選項可以有也可以沒有name參數。使用沒有name參數的references選項將打開或者關閉引用信息的收集(然而繼續收集其它瀏覽信息)。例如:

#pragma component(browser, off, references)

終止編譯程序收集引用信息。

使用有name和off參數的references選項將阻止從瀏覽信息窗口中出現引用到的名字。用這個語法將忽略你不感興趣的名字和類型從而減少瀏覽信息文件的大小。例如:

#pragma component(browser, off, references, DWORD)

從這一點以後忽略DWORD的引用。你能夠用on恢復DWORD的引用收集:

#pragma component(browser, on, references, DWORD)

這是唯一的方法可以恢復收集指定名字的引用,你必須顯式地打開任何你關閉的名字。

爲了防止預處理程序擴展名字(就像擴展NULL到0),用引號括起來:

#pragma component(browser, off, references, "NULL")

8-2
 
最小化重建(
Minimal Rebuild

Visual C++的最小化重建功能要求編譯程序創建並保存需要大量磁盤空間的C++類依賴信息。爲了節省磁盤空間,你能夠在你不需要收集依賴信息時使用#pragma component(minrebuild,off),例如,沒有改變過頭文件。在未修改過的類之後插入#pragma component(minrebuild,on)重新打開依賴信息。

詳見Enable Minimal Rebuild(/Gm)編譯程序選項。

9  data_seg
#pragma da
ta_seg( ["section-name"[, "section-class"] ] )

指定數據的默認段。例如:

#pragma data_seg( "MY_DATA" )

導致在#pragma語句後分配的數據保存在一個叫做MY_DATA的段中。

用data_seg編譯指示分配的數據不包含任何關於其位置的信息。

第二個參數section-class是用於兼容2.0版本以前的Visual C++的,現在將忽略它。

10  function
#pragma function( function1 [, function2, ...] )

指定必須生成對編譯指示中參數列表內函數的調用。如果你使用intrinsic編譯指示(或者/Oi)來告訴編譯程序生成內含函數(內含函數如同嵌入代碼一樣生成,不作爲一個函數調用),你能夠用function編譯指示顯式地強迫函數調用。當遇到一個function編譯指示,它將在其後面遇到的第一個包含有內含函數的函數定義處生效。其持續作用到源文件的尾部或者出現對同一個內含函數指定intrinsic編譯指示。function編譯指示只能用於函數外——在全局層次。

爲了列出具有內含形式的函數表,參見#pragma intrinsic。

11  hdrstop
#pragma hdrstop [( "filename" )] 

控制預編譯頭文件的工作方式。filename是要使用或者創建(依賴於是否指定了/Yu或/Yc)預編譯頭文件的名字。如果 filename不包括一個指定路徑,將假定預編譯頭文件和源文件處於同一個目錄中。當指定自動預編譯頭文件選項/YX時,所有指定的文件名將被忽略。

如果有/YX或者/Yc選項,而且C或C++文件包含了一個hdrstop編譯指示時,編譯程序保存編譯指示之前的編譯狀態。編譯指示之後的編譯狀態不被保存。

hdrstop編譯選項不能出現在一個頭文件內。它只能出現在源文件的文件級,它也不能出現在任何數據或者函數的說明或定義之中。

注意,除非指定沒有文件名的/YX選項或者/Yu或/Yc選項,否則hdrstop編譯指示將被忽略。

用一個文件名命名要保存編譯狀態的預編譯頭文件。在hdrstop和filename之間的空格是可選的。在hdrstop編譯指示中的文件名是一個字符串,這樣它服從於C或C++的字符串規則。特別的,你必須像下面例子裏面顯示的用引號括起來。

#pragma hdrstop( "c:\projects\include\myinc.pch" )

預編譯頭文件的文件名按照如下規則決定,按照優先次序:

/Fp編譯程序選項的參數;

由#pragma hdrstop的filename參數;

原文件名的基本文件名加上.PCH擴展名。

12  include_alias
#pragma include_alias( "long_filename", "short_filename" )

#pragma include_alias( <long_filename>, <short_filename> )

指定作爲long_filename別名的short_filename。一些文件系統允許超出8.3FAT文件系統限制的長頭文件名。編譯程序不能簡單地將長文件名截斷爲8.3名字,因爲長頭文件名的前8個字符可能不是唯一的。無論何時編譯程序遇到long_filename串,它代替short_filename,並且用short_filename搜索頭文件。這個編譯指示必須出現在相應的#include指示之前。例如:

// First eight characters of these two files not unique.

#pragma include_alias( "AppleSystemHeaderQuickdraw.h", "quickdra.h" )

#pragma include_alias( "AppleSystemHeaderFruit.h", "fruit.h" )

#pragma include_alias( "GraphicsMenu.h", "gramenu.h" )

 

#include "AppleSystemHeaderQuickdraw.h"

#include "AppleSystemHeaderFruit.h"

#include "GraphicsMenu.h"

這個別名在搜索時精確匹配,包括拼寫和雙引號、尖括號。include_alias編譯指示在文件名上執行簡單的字符串匹配,不進行其它的文件名驗證。例如,給出下列指示:

#pragma include_alias("mymath.h", "math.h")

#include "./mymath.h"

#include "sys/mymath.h"

並不執行別名替代,因爲頭文件名字符串沒有精確匹配。另外,在/Yu,/Yc和/YX編譯程序選項,或hdrstop編譯指示中作爲參數的頭文件名不被替換。例如,如果你的源文件包含下列指示:

#include <AppleSystemHeaderStop.h>

相應的編譯程序選項必須是:

/YcAppleSystemHeaderStop.h

你能夠用include-_alias編譯指示將任何頭文件映射到其它文件。例如:

#pragma include_alias( "api.h", "c:\version1.0\api.h" )

#pragma include_alias( <stdio.h>, <newstdio.h> )

#include "api.h"

#include <stdio.h>

不要混淆用雙引號和尖括號括起來的文件名。例如,給出上面的#pragma include_alias指示時,在下面的#include指示中編譯程序不執行替換。

#include <api.h>

#include "stdio.h"

還有,下面的指示將產生一個錯誤:

#pragma include_alias(<header.h>, "header.h")  // Error

注意,在錯誤信息中報告的文件名,或者預定義宏__FILE__的值,是執行替換以後的文件名。例如,在下列指示之後:

#pragma include_alias( "VeryLongFileName.H", "myfile.h" )

#include "VeryLongFileName.H"

文件VeryLongFileName.H產生下列錯誤信息:

myfile.h(15) : error C2059 : syntax error

還要注意的是不支持傳遞性。給出下面的指示:

#pragma include_alias( "one.h", "two.h" )

#pragma include_alias( "two.h", "three.h" )

#include "one.h"

編譯程序將搜索two.h而不是three.h。

13  init_seg
C++特有

#pragma init_seg({ compiler | lib | user | "section-name" [, "func-name"]} )

指定影響啓動代碼執行的關鍵字或代碼段。因爲全局靜態對象的初始化可以包含執行代碼,所以你必須指定一個關鍵字來定義什麼時候構造對象。在使用需要初始化的動態連接庫(DLL)或程序庫時使用init_seg編譯指示是尤其重要的。

init_seg編譯指示的選項有:

13-1
 
compiler
由Microsoft C運行時間庫保留。在這個組中的對象將第一個構造。

13-2
 
lib
用於第三方類庫開發者的初始化。在這個組中的對象將在標記爲構造compiler的對象之後,其它對象之前構造。

13-3
 
user
用於任何其它用戶。在這個組中的對象將最後構造。

13-4
 
section-name
允許顯式地指定初始化段。在用戶指定的section-name中的對象將不會隱式地構造,而它們的地址將會被放置在由section-name命名的段中。

13-5
 
func-name
指定當程序退出時,作爲atexit函數調用的函數。這個函數必須具有和atexit函數相同的形式:

int funcname(void (__cdecl *)(void));

如果你需要延遲初始化,你能夠選擇指定顯式的段名。隨後你必須調用每個靜態對象的構造函數。

14  inline_depth
#pragma inline_depth( [0... 255] )

通過控制能夠被擴展的一系列函數調用(從0到255次)來控制嵌入函數擴展的發生次數,這個編譯指示控制用inline,__inline標記的或在/Ob2選項下能自動嵌入的嵌入函數。

inline_depth編譯指示控制能夠被擴展的一系列函數調用。例如,如果嵌入深度是4,並且如果A調用B然後調用C,所有的3次調用都將做嵌入擴展。然而,如果設置的最近一次嵌入深度是2,則只有A和B被擴展,而C仍然作爲函數調用。

爲了使用這個編譯指示,你必須設置編譯程序選項/Ob爲1或者2。用這個編譯指示指定的深度設定在該指示後面的第一個函數開始生效。如果你在括號內不指定一個值,inline_depth設置嵌入深度到默認值8。

在擴展時,嵌入深度可以被減少而不能被增加。如果嵌入深度是6,同時在擴展過程中預處理程序遇到一個inline_depth編譯指示設置爲8,則深度保持爲6。

嵌入深度0將拒絕嵌入擴展,深度255將設置在嵌入擴展時沒有限制。如果用一個沒有指定值的編譯指示,則使用爲默認值。

15  inline_recursion
#pragma inline_recursion( [{on | off}] )

控制直接或者相互間的遞歸函數調用式的嵌入擴展。用這個編譯指示控制用inline,__inline標記的或在/Ob2選項下能自動嵌入的嵌入函數。使用這個編譯指示需要設置編譯程序選項/Ob爲1或者2。默認的inline_recursion狀態是off。這個編譯指示在出現該編譯指示之後第一個函數調用起作用,並不影響函數的定義。

inline_recursion編譯指示控制如何擴展遞歸函數。如果inline_recursion是off,並且如果一個嵌入函數調用了它自己(直接的或者間接的),函數將僅僅擴展一次。如果inline_recursion是on,函數將擴展多次直到達到inline_depth的值或者容量限制。

16  intrinsic
#pragma intrinsic( function1 [, function2, ...] )

指定對在編譯指示參數表中函數調用是內含的。編譯程序像嵌入代碼一樣生成內含函數,而不是函數調用。下面列出了具有內含形式的庫函數。一旦遇到intrinsic編譯指示,它從第一個包含指定內含函數的函數定義開始起作用。作用持續到源文件尾部或者出現包含相同內含函數的function編譯指示。intrinsic編譯指示只能用在函數定義外——在全局層次。

下列函數具有內含形式:

_disable
 _enable
 _inp
 _inpw
 _lrotl
 _lrotr
 
_outp
 _outpw
 _rotl
 _rotr
 _strset
 abs
 
fabs
 labs
 memcmp
 memcpy
 memset
 strcat
 
strcmp
 strcpy
 strlen
 
 
 
 

使用內含函數的程序更快,因爲它們沒有函數調用的額外代價,然而因爲有附加的代碼生成,可能比較大。

注意,_alloca和setjmp函數總是內含的,這個行爲不受intrinsic編譯指示影響。

下列浮點函數沒有內含形式。然而它們具有直接將參數通過浮點芯片傳送而不是推入程序堆棧的版本。

acos
 asin
 cosh
 fmod
 pow
 sinh
 
tanh
 
 
 
 
 
 

當你同時指定/Oi和/Og編譯程序選項(或者任何包含/Og,/Ox,/O1和/O2的選項)時下列浮點函數具有真正的內含形式。

atan
 exp
 log10
 sqrt
 atan2
 log
 
sin
 tan
 cos      
 
 
 
 

你可以用編譯程序選項/Op或/Za來覆蓋真內含浮點選項的生成。在這種情況下,函數會像一般庫函數一樣被生成,同時直接將參數通過浮點芯片傳送而不是推入程序堆棧。

17  message
#pragma message( messagestring )

不中斷編譯,發送一個字符串文字量到標準輸出。message編譯指示的典型運用是在編譯時顯示信息。

下面的代碼段用message編譯指示在編譯過程中顯示一條信息:

#if _M_IX86 == 500

#pragma message( "Pentium processor build" )

#endif

messagestring參數可以是一個能夠擴展成字符串文字量的宏,並且你能夠用字符串文字量和宏的任何組合來構造。例如,下面的語句顯示被編譯文件的文件名和文件最後一次修改的日期和時間。

#pragma message( "Compiling " __FILE__ )

#pragma message( "Last modified on " __TIMESTAMP__ )

18  once
#pragma on
ce

指定在創建過程中該編譯指示所在的文件僅僅被編譯程序包含(打開)一次。該編譯指示的一種常見用法如下:

//header.h

#pragma once

// Your C or C++ code would follow:

19  optimize
僅在專業版和企業版中存在

#pragma optimize( "[optimization-list]", {on | off} )

代碼優化僅有Visual C++專業版和企業版支持。詳見Visual C++ Edition。

指定在函數層次執行的優化。optimize編譯選項必須在函數外出現,並且在該編譯指示出現以後的第一個函數定義開始起作用。on和off參數打開或關閉在optimization-list指定的選項。

optimization-list能夠是0或更多個在表2.2中給出的參數:

表 2.2   optimize編譯指示的參數

參數
 優化類型
 
a
 假定沒有別名。
 
g
 允許全局優化。
 
p
 增強浮點一致性。
 
s 或 t
 指定更短或者更快的機器代碼序列。
 
w
 假定在函數調用中沒有別名。
 
y
 在程序堆棧中生成框架指針。
 

這些和在/O編譯程序選項中使用的是相同的字母。例如:

#pragma optimize( "atp", on )

用空字符串("")的optimize編譯指示是一種特別形式。它要麼關閉所有的優化選項,要麼恢復它們到原始(或默認)的設定。

#pragma optimize( "", off )

.

.

.

#pragma optimize( "", on )

20  pack
#pragma pack( [ n] )

指定結構和聯合成員的緊縮對齊。儘管用/Zp選項設定整個翻譯單元的結構和聯合成員的緊縮對齊,可以用pack編譯指示在數據說明層次設定緊縮對齊。從出現該編譯指示後的第一個結構或者聯合說明開始生效。這個編譯指示不影響定義。

當你使用#pragma pack(n),其中n是1,2,4,8或者16,第一個以後的每個結構成員保存在較小的成員類型或者n字節邊界上。如果你使用沒有參數的#pragma pack,結構成員將被緊縮到由/Zp指定的值。默認的/Zp緊縮的大小是/Zp8。

編譯程序還支持下面的增強語法:

#pragma pack( [ [ { push | pop}, ] [  identifier, ] ] [ n ] )

該語法允許你將使用不同緊縮編譯指示的組件合併到同一個翻譯單元內。

每次出現有push參數的pack編譯指示將保存當前的緊縮對齊值到一個內部的編譯程序堆棧。編譯指示的參數列表從左向右讀取。如果你使用了push,當前緊縮值被保存。如果你提供了一個n值,這個值將成爲新的緊縮值。如果你指定了一個你選定的標示符,這個標示符將和新的緊縮值關聯。

每次出現有pop參數的pack編譯指示從內部編譯程序堆棧頂部取出一個值並將那個值作爲新的緊縮對齊。如果你用了pop,而內部編譯程序堆棧是空的,對齊值將從命令行得到,同時給出一個警告。如果你用了pop並指定了n的值,那個值將成爲新的緊縮值。如果你用了pop並指定了一個標示符,將移去所有保存在堆棧中的的值直到匹配的找到匹配的標示符,和該標示符關聯的緊縮值也被從堆棧中移出來成爲新的緊縮值。如果沒有找到匹配的標示符,將從命令行獲取緊縮值併產生一個1級警告。默認的緊縮對齊是8。

pack編譯指示的新的增強功能允許你編寫頭文件保證在使用頭文件之前和其後的緊縮值是一樣的:

/* File name: include1.h

*/

#pragma pack( push, enter_include1 )

/* Your include-file code ... */

#pragma pack( pop, enter_include1 )

/* End of include1.h */

在前面的例子中,進入頭文件時將當前緊縮值和標示符enter_include1關聯並推入,被記住。在頭文件尾部的pack編譯選項移去所有在頭文件中可能遇到的緊縮值並移去和enter_include1關聯的緊縮值。這樣頭文件保證了在使用頭文件之前和其後的緊縮值是一樣的。

新功能也允許你在你的代碼內用pack編譯指示爲不同的代碼,例如頭文件設定不同的緊縮對齊。

#pragma pack( push, before_include1 )

#include "include1.h"

#pragma pack( pop, before_include1 )

在上一個例子中,你的代碼受到保護,防止了在include.h中的任何緊縮值的改變。

21  pointers_to_members
C++特有

#pragma pointers_to_members(pointer-declaration, [most-general-representation] )

指定是否能夠在相關類定義之前說明一個指向類成員的指針,並且用於控制指針的大小和解釋指針的代碼。你能夠在你的源代碼中使用pointers_to_members編譯知識來代替/vmx編譯程序選項。

pointer-declaration參數指出是否在相關函數定義之前或其後你已經說明了一個指向成員的指針。pointer-declaration參數是下面兩個符號之一:

參數
 說明
 
full_generality
 生成安全的,但是有時不能優化的代碼。如果有一些指向成員的指針在相關類定義之前說明,你要用full_generality。這個參數總是使用由most-general-representation指定的指針表示方式。
 
best_case
 對於所有指向成員的指針用最佳的表示方式生成安全的,優化的代碼。需要在說明一個指向類成員指針之前定義類。默認是best_case。
 

most-general-representaion參數指出在一個翻譯單元中編譯程序能夠安全引用任何指向類成員指針的最小指針表示方式。這個參數可以是下列之一:

參數
 說明
 
single_inheritance
 最普通的表示方式是單繼承,指向成員函數。如果用於指向具有多重或者虛擬繼承方式類成員的指針,將產生一個錯誤。
 
multi_inheritance
 最普通的表示方式是多重繼承,指向成員函數。如果用於指向具有虛擬繼承方式類成員的指針,將產生一個錯誤。
 
virtual_inheritance
 最普通的表示方式是虛擬繼承,指向成員函數。不會產生錯誤。當使用#pragma pointers_to_members (full_generality)時這是默認的參數。
 

22  setlocale
#pragma setlocale( "locale-string" )

定義用於翻譯寬字符常數和字符串文字量時用的地區(國家和語言)。由於用於從多字節字符轉換到寬字符的算法根據地區或者由於在運行可執行程序不同的地方進行編譯而不同,這個編譯指示提供一種在編譯時指定目標地區的方式。這保證寬字符字符串將以正確的格式保存。默認的locale-string是“C”。“C”地區將字符串中的每個字符作爲wchar_t(即unsigned int)映射其值。

23  vtordisp
C++特有

#pragma vtordisp({on | off} )

允許隱藏的附加vtordisp構造函數/析構函數替換成員。vtordisp編譯指示僅能夠用於具有虛擬基類的代碼。如果派生類從一個虛擬基類重載了一個虛擬函數,並且如果派生類的構造函數或析構函數用指向虛擬基類的指針調用了這個函數,編譯程序將根據虛擬基類在類中引入一個附加的隱藏“vtordisp”域。

vtodisp編譯選項影響它後面的類佈局。/vd0和/vd1選項爲整個模塊指定了相同的行爲。指定off將禁止隱藏的vtordisp成員,指定on(默認)將在它們需要的時候允許vtordisp。僅在不可能出現類的構造函數和析構函數通過this指針調用其指向對象中的虛擬函數時才關閉vtordisp。

#pragma vtordisp( off )

class GetReal : virtual public { ... };

#pragma vtordisp( on )

24  warning
#pragma warning( warning-specifier : warning-number-list [,warning-specifier : warning-number-list...] )

#pragma warning( push[ , n ] )

#pragma warning( pop )

允許有選擇地修改編譯程序警告信息的行爲。

warning-specifier能夠是下列值之一:

warning-specifier
 含義
 
on
ce
 只顯示指定信息一次。
 
default
 對指定信息應用默認的編譯程序選項。
 
1,2,3,4
 對指定信息引用給定的警告等級。
 
disable
 不顯示指定信息。
 
error
 對指定信息作爲錯誤顯示。
 

warning-number_list能夠包含任何警告編號。如下,在一個編譯指示中可以指定多個選項:

#pragma warning( disable : 4507 34; once : 4385; error : 164 )

這等價於:

#pragma warning( disable : 4507 34 )  // Disable warning messages

                                            //  4507 and 34.

#pragma warning( once : 4385 )         // Issue warning 4385

                                            //  only once.

#pragma warning( error : 164 )         // Report warning 164

                                            //  as an error.

對於那些關於代碼生成的,大於4699的警告標號,warning編譯指示僅在函數定義外時有效。如果指定的警告編號大於4699並且用於函數內時被忽略。下面例子說明了用warning編譯指示禁止、然後恢復有關代碼生成警告信息的正確位置:

int a;

#pragma warning( disable : 4705 )

void func()

{

    a;

}

#pragma warning( default : 4705 )

warning編譯指示也支持下面語法:

#pragma warning( push [ ,n ] )

#pragma warning( pop )

這裏n表示警告等級(1到4)。

warning(push)編譯指示保存所有警告的當前警告狀態。warning(push,n)保存所有警告的當前狀態並將全局警告等級設置爲n。

warning(pop)彈出最後一次推入堆棧中的警告狀態。任何在push和pop之間改變的警告狀態將被取消。考慮下面的例子:

#pragma warning( push )

#pragma warning( disable : 4705 )

#pragma warning( disable : 4706 )

#pragma warning( disable : 4707 )

// Some code

#pragma warning( pop )

在這些代碼的結束,pop恢復了所有警告的狀態(包括4705,4706和4707)到代碼開始時候的樣子。

當你編寫頭文件時,你能用push和pop來保證任何用戶修改的警告狀態不會影響正常編譯你的頭文件。在頭文件開始的地方使用push,在結束地方使用pop。例如,假定你有一個不能順利在4級警告下編譯的頭文件,下面的代碼改變警告等級到3,然後在頭文件的結束時恢復到原來的警告等級。

#pragma warning( push, 3 )

// Declarations/ definitions

#pragma warning( pop )

 三 編譯選項的控制:

編譯選項對於一些工程非常有效,可以控制多語言版本,多種編譯版本,多種編譯方式等。

選擇菜單 Build->Configurations,增加一個工程配置,在Configuration中輸入 Debug English 在 Copy Setting from 中選擇 Debug 就可以(見下圖),使用相同的方法,再增加一個 Debug Chinese 配置,並把原來的 Debug 刪除。

選擇菜單 Project->Settings,在左邊的 Setting For 中選擇 Debug Chinese 在 Generatl 屬性頁的 Intermediate files 中輸入 Debug Chinese,在 Output files 中輸入 Chinese。在 Resource 屬性頁的 Resource file name 中輸入 Debug Chinese/Example_Ch.res,(見下圖)其它缺省就行

 

四 注意一些問題

#pragma once 與 #ifndef 解析  爲了避免同一個文件被include多次(即防止編譯多次),C/C++中有兩種方式,一種是#ifndef方式,一種是#pragma once方式。在能夠支持這兩種方式的編譯器上,二者並沒有太大的區別,但是兩者仍然還是有一些細微的區別。
    方式一:

    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // 聲明、定義語句
    #endif
 

    方式二:


    #pragma once
    ... ... // 聲明、定義語句
 
    #ifndef的方式受C/C++語言標準支持。它不光可以保證同一個文件不會被包含多次,也能保證內容完全相同的兩個文件(或者代碼片段)不會被不小心同時包含。
    當然,缺點就是如果不同頭文件中的宏名不小心“撞車”,可能就會導致你看到頭文件明明存在,編譯器卻硬說找不到聲明的狀況——這種情況有時非常讓人抓狂。
    由於編譯器每次都需要打開頭文件才能判定是否有重複定義,因此在編譯大型項目時,ifndef會使得編譯時間相對較長,因此一些編譯器逐漸開始支持#pragma once的方式。

    #pragma once一般由編譯器提供保證:同一個文件不會被包含多次。注意這裏所說的“同一個文件”是指物理上的一個文件,而不是指內容相同的兩個文件。你無法對一個頭文件中的一段代碼作pragma once聲明,而只能針對文件。
    其好處是,你不必再費勁想個宏名了,當然也就不會出現宏名碰撞引發的奇怪問題。大型項目的編譯速度也因此提高了一些。
    對應的缺點就是如果某個頭文件有多份拷貝,本方法不能保證他們不被重複包含。當然,相比宏名碰撞引發的“找不到聲明”的問題,這種重複包含很容易被發現並修正。

    #pragma once方式產生於#ifndef之後,因此很多人可能甚至沒有聽說過。目前看來#ifndef更受到推崇。因爲#ifndef受C/C++語言標準的支持,不受編譯器的任何限制;而#pragma once方式卻不受一些較老版本的編譯器支持,一些支持了的編譯器又打算去掉它,所以它的兼容性可能不夠好。一般而言,當程序員聽到這樣的話,都會選擇#ifndef方式,爲了努力使得自己的代碼“存活”時間更久,通常寧願降低一些編譯性能,這是程序員的個性,當然這是題外話啦。

    還看到一種用法是把兩者放在一起的:

    #pragma once
    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // 聲明、定義語句
    #endif
 
    看起來似乎是想兼有兩者的優點。不過只要使用了#ifndef就會有宏名衝突的危險,也無法避免不支持#pragma once的編譯器報錯,所以混用兩種方法似乎不能帶來更多的好處,倒是會讓一些不熟悉的人感到困惑。

    選擇哪種方式,應該在瞭解兩種方式的情況下,視具體情況而定。只要有一個合理的約定來避開缺點,我認爲哪種方式都是可以接受的。而這個已經不是標準或者編譯器的責任了,應當由程序員自己或者小範圍內的開發規範來搞定。

    btw:我看到GNU的一些討論似乎是打算在GCC 3.4(及其以後?)的版本取消對#pragma once的支持。不過事實上,我手上的GCC 3.4.2和GCC 4.1.1仍然支持#pragma once,甚至沒有deprecation warning,倒是GCC2.95會對#pragma once提出warning。
    VC6及其以後版本亦提供對#pragma once方式的支持,這一特性應該基本穩定下來了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章