#pragma 的使用
盡管 C 和 C++ 都已經(jīng)有標(biāo)準(zhǔn),但是幾乎每個(gè)編譯器 (廣義,包含連接器等) 擴(kuò)展一些 C/C++ 關(guān)鍵字。
合理地應(yīng)用這些關(guān)鍵字,有時(shí)候能使我們的工作非常方便。下面隨便說說 Visual C++ 中 #pragma
指示符的使用。
一、用#pragma導(dǎo)出DLL函數(shù)
傳統(tǒng)的到出 DLL 函數(shù)的方法是使用模塊定義文件 (.def),Visual C++ 提供了更簡(jiǎn)潔方便的方法,
那就是“__declspec()”關(guān)鍵字后面跟“dllexport”,告訴連接去要導(dǎo)出這個(gè)函數(shù),例如:
__declspec(dllexport) int __stdcall MyExportFunction(int iTest);
把“__declspec(dllexport)”放在函數(shù)聲明的最前面,連接生成的 DLL 就會(huì)導(dǎo)出函數(shù)
“_MyExportFunction@4”。
上面的導(dǎo)出函數(shù)的名稱也許不是我的希望的,我們希望導(dǎo)出的是原版的“MyExportFunction”。
還好,VC 提供了一個(gè)預(yù)處理指示符“#pragma”來指定連接選項(xiàng) (不僅僅是這一個(gè)功能,還有很多指示功能) ,
如下:
#pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")
這下就天如人愿了:)。如果你想指定導(dǎo)出的順序,或者只將函數(shù)導(dǎo)出為序號(hào),沒有 Entryname,
這個(gè)預(yù)處理指示符 (確切地說是連接器) 都能夠?qū)崿F(xiàn),看看 MSDN 的語法說明:
/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]
@ordinal 指定順序;NONAME 指定只將函數(shù)導(dǎo)出為序號(hào);DATA 關(guān)鍵字指定導(dǎo)出項(xiàng)為數(shù)據(jù)項(xiàng)。
二、指示文件只包含一次
在頭文件中,一般在整個(gè)工程中我們只要包含一次就夠了,但是如果我在多個(gè) .c/.cpp 文件中都要包
含著個(gè)頭文件,比如 Windows.h,那很多聲明等等豈不是有兩次了?解決這個(gè)問題的傳統(tǒng)的方法是在頭文件
開始出用
#define 定義一個(gè)宏,比如 Windows.h 中:
#ifndef _WINDOWS_#define _WINDOWS_
P> 然后在文件結(jié)為加上 #endif,這樣就可以避免被包含多次。但是這樣的后果是代碼的可讀性較差
(個(gè)人觀點(diǎn)),VC 給我們提供了另外一個(gè)途徑,那就是在文件的前面加上:#pragma once 是不是很方便?
三、使警告無效 有時(shí)候我們不得不對(duì)變量進(jìn)行強(qiáng)制轉(zhuǎn)換,由此引來編譯器的一番警告,特別是 C++ 中
,類型檢查相對(duì)于 C 更為嚴(yán)格。這雖然不影響什么,但是看起來多不爽——我是故意要這樣的,
你警告什么!:)這時(shí)候你看到警告類型,
比如“warning C4311: “類型轉(zhuǎn)換” : 從“HHOOK”到“BOOL”的指針截?cái)?#8221;,在前面加上:
#pragma warning(disable: 4311) 編譯器就沒話說了:)。
四、指定連接要使用的庫(kù) 比如我們連接的時(shí)候用到了 WSock32.lib,你當(dāng)然可以
不辭辛苦地把它加入到你的工程中。但是我覺得更方便的方法是使用 #pragma 指示符,指定要連接的庫(kù):
#pragma comment(lib, "WSock32.lib")五、顯示編譯消息 沒多少用處,舉個(gè)例子吧:
#ifdef _DEBUG#pragma message
("編譯連接為調(diào)試模式...")#endif // _DEBUG
在所有的預(yù)處理指令中,#Pragma 指令可能是最復(fù)雜的了,它的作用是設(shè)定編譯器的狀態(tài)或者是指示
編譯器完成一些特定的動(dòng)作。#pragma
指令對(duì)每個(gè)編譯器給出了一個(gè)方法,在保持與C和C++語言完全兼容的情況下,給出主機(jī)或操作系統(tǒng)專有
的特征。依據(jù)定義,編譯指示是機(jī)器或操作系統(tǒng)專有的,且對(duì)于每個(gè)編譯器都是不同的。
其格式一般為: #Pragma Para
其中Para 為參數(shù),下面來看一些常用的參數(shù)。
(1)message 參數(shù)。 Message 參數(shù)是我最喜歡的一個(gè)參數(shù),它能夠在編譯信息輸出窗
口中輸出相應(yīng)的信息,這對(duì)于源代碼信息的控制是非常重要的。其使用方法為:
#Pragma message(“消息文本”)
當(dāng)編譯器遇到這條指令時(shí)就在編譯輸出窗口中將消息文本打印出來。
當(dāng)我們?cè)诔绦蛑卸x了許多宏來控制源代碼版本的時(shí)候,我們自己有可能都會(huì)忘記有沒有正確的設(shè)
置這些宏,此時(shí)我們可以用這條指令在編譯的時(shí)候就進(jìn)行檢查。假設(shè)我們希望判斷自己有沒有在源
代碼的什么地方定義了
_X86這個(gè)宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
當(dāng)我們定義了_X86這個(gè)宏以后,應(yīng)用程序在編譯時(shí)就會(huì)在編譯輸出窗口里顯示“_
X86 macro activated!”。我們就不會(huì)因?yàn)椴挥浀米约憾x的一些特定的宏而抓耳撓腮了
。
(2)另一個(gè)使用得比較多的pragma參數(shù)是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能夠設(shè)置程序中函數(shù)代碼存放的代碼段,當(dāng)我們開發(fā)驅(qū)動(dòng)程序的時(shí)候就會(huì)使用到它。
(3)#pragma once (比較常用)
只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實(shí)際上在VC6中就已經(jīng)有了,
但是考慮到兼容性并沒有太多的使用它。
(4)#pragma hdrstop表示預(yù)編譯頭文件到此為止,后面的頭文件不進(jìn)行預(yù)編譯。BCB可以預(yù)編譯頭文件以加
快鏈接的速度,但如果所有頭文件都進(jìn)行預(yù)編譯又可能占太多磁盤空間,所以使用這個(gè)選項(xiàng)排除一些頭文件。
有時(shí)單元之間有依賴關(guān)系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用
#pragma startup指定編譯優(yōu)先級(jí),如果使用了#pragma package(smart_init) ,BCB就會(huì)根據(jù)優(yōu)先級(jí)的
大小先后編譯。
(5)#pragma resource "*.dfm"表示把*.dfm文件中的資源加入工程。*.dfm中包括窗體
外觀的定義。
(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等價(jià)于:
#pragma warning(disable:4507 34) // 不顯示4507和34號(hào)警告信息
#pragma warning(once:4385) // 4385號(hào)警告信息僅報(bào)告一次
#pragma warning(error:164) // 把164號(hào)警告信息作為一個(gè)錯(cuò)誤。
同時(shí)這個(gè)pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這里n代表一個(gè)警告等級(jí)(1---4)。
#pragma warning( push )保存所有警告信息的現(xiàn)有的警告狀態(tài)。
#pragma warning( push, n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告
等級(jí)設(shè)定為n。
#pragma warning( pop )向棧中彈出最后一個(gè)警告信息,在入棧和出棧之間所作的
一切改動(dòng)取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
該指令將一個(gè)注釋記錄放入一個(gè)對(duì)象文件或可執(zhí)行文件中。
常用的lib關(guān)鍵字,可以幫我們連入一個(gè)庫(kù)文件。
#pragma comment( comment-type [,"commentstring"] )
comment-type是一個(gè)預(yù)定義的標(biāo)識(shí)符,指定注釋的類型,應(yīng)該是compiler,exestr,lib,linker之一。
commentstring是一個(gè)提供為comment-type提供附加信息的字符串,
Remarks:
1、compiler:放置編譯器的版本或者名字到一個(gè)對(duì)象文件,該選項(xiàng)是被linker忽略的。
2、exestr:在以后的版本將被取消。
3、lib:放置一個(gè)庫(kù)搜索記錄到對(duì)象文件中,這個(gè)類型應(yīng)該是和commentstring(指定你要Liner搜索的lib的名稱和路徑)
這個(gè)庫(kù)的名字放在Object文件的默認(rèn)庫(kù)搜索記錄的后面,linker搜索這個(gè)這個(gè)庫(kù)就像你在命令行輸入這個(gè)命令一樣。你可以
在一個(gè)源文件中設(shè)置多個(gè)庫(kù)記錄,它們?cè)趏bject文件中的順序和在源文件中的順序一樣。如果默認(rèn)庫(kù)和附加庫(kù)的次序是需要
區(qū)別的,使用Z編譯開關(guān)是防止默認(rèn)庫(kù)放到object模塊。
4、linker:指定一個(gè)連接選項(xiàng),這樣就不用在命令行輸入或者在開發(fā)環(huán)境中設(shè)置了。
只有下面的linker選項(xiàng)能被傳給Linker.
-
/DEFAULTLIB
-
/EXPORT
-
/INCLUDE
-
/MANIFESTDEPENDENCY
-
/MERGE
-
/SECTION
(1)/DEFAULTLIB:library
/DEFAULTLIB 選項(xiàng)將一個(gè) library 添加到 LINK 在解析引用時(shí)搜索的庫(kù)列表。用 /DEFAULTLIB 指定的庫(kù)在命令行上指定的庫(kù)之后和 .obj 文件中指定的默認(rèn)庫(kù)之前被搜索。
忽略所有默認(rèn)庫(kù) (/NODEFAULTLIB) 選項(xiàng)重寫 /DEFAULTLIB:library。如果在兩者中指定了相同的 library 名稱,忽略庫(kù) (/NODEFAULTLIB:library) 選項(xiàng)將重寫 /DEFAULTLIB:library。
(2)/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]
使用該選項(xiàng),可以從程序?qū)С龊瘮?shù),以便其他程序可以調(diào)用該函數(shù)。也可以導(dǎo)出數(shù)據(jù)。通常在 DLL 中定義導(dǎo)出。entryname 是調(diào)用程序要使用的函數(shù)或數(shù)據(jù)項(xiàng)的名稱。ordinal 在導(dǎo)出表中指定范圍在 1 至 65,535 的索引;如果沒有指定 ordinal,則 LINK 將分配一個(gè)。NONAME 關(guān)鍵字只將函數(shù)導(dǎo)出為序號(hào),沒有 entryname。
DATA 關(guān)鍵字指定導(dǎo)出項(xiàng)為數(shù)據(jù)項(xiàng)??蛻舫绦蛑械臄?shù)據(jù)項(xiàng)必須用 extern __declspec(dllimport) 來聲明。
有三種導(dǎo)出定義的方法,按照建議的使用順序依次為:
-
源代碼中的 __declspec(dllexport)
-
.def 文件中的 EXPORTS 語句
-
LINK 命令中的 /EXPORT 規(guī)范
所有這三種方法可以用在同一個(gè)程序中。LINK 在生成包含導(dǎo)出的程序時(shí)還創(chuàng)建導(dǎo)入庫(kù),除非生成中使用了 .exp 文件。
LINK 使用標(biāo)識(shí)符的修飾形式。編譯器在創(chuàng)建 .obj 文件時(shí)修飾標(biāo)識(shí)符。如果 entryname 以其未修飾的形式指定給鏈接器(與其在源代碼中一樣),則 LINK 將試圖匹配該名稱。如果無法找到唯一的匹配名稱,則 LINK 發(fā)出錯(cuò)誤信息。當(dāng)需要將標(biāo)識(shí)符指定給鏈接器時(shí),請(qǐng)使用 Dumpbin 工具獲取該標(biāo)識(shí)符的修飾名形式。
(3)/INCLUDE:symbol
/INCLUDE 選項(xiàng)通知鏈接器將指定的符號(hào)添加到符號(hào)表。
若要指定多個(gè)符號(hào),請(qǐng)?jiān)诜?hào)名稱之間鍵入逗號(hào) (,)、分號(hào) (;) 或空格。在命令行上,對(duì)每個(gè)符號(hào)指定一次 /INCLUDE:symbol。
鏈接器通過將包含符號(hào)定義的對(duì)象添加到程序來解析 symbol。該功能對(duì)于添包含不會(huì)鏈接到程序的庫(kù)對(duì)象非常有用。用該選項(xiàng)指定符號(hào)將通過 /OPT:REF 重寫該符號(hào)的移除。
我們經(jīng)常用到的是#pragma comment(lib,"*.lib")這類的。
#pragma comment(lib,"Ws2_32.lib")表示鏈接Ws2_32.lib這個(gè)庫(kù)。
和在工程設(shè)置里寫上鏈入Ws2_32.lib的效果一樣,不過這種方法寫的
程序別人在使用你的代碼的時(shí)候就不用再設(shè)置工程settings了