#pragma能夠說是C++中最複雜的預處理指令了,下面是最經常使用的幾個#pragma指令:html
#pragma comment(lib,"XXX.lib")緩存
表示連接XXX.lib這個庫,和在工程設置裏寫上XXX.lib的效果同樣。函數
#pragma comment(linker,"/ENTRY:main_function")工具
表示指定連接器選項/ENTRY:main_functionspa
#pragma once操作系統
表示這個文件只被包含一次命令行
#pragma warning(disable:4705)code
表示屏蔽警告4705component
C和C++程序的每次執行都支持其所在的主機或操做系統所具備的一些獨特的特色。例如,有些程序須要精確控制數據存放的內存區域或控制某個函orm
數接收的參數。#pragma爲編譯器提供了一種在不一樣機器和操做系統上編譯以保持C和C++徹底兼容的方法。#pragma是由機器和相關的操做系統定義
的,一般對每一個編譯器來講是不一樣的。
若是編譯器遇到不認識的pragma指令,將給出警告信息,而後繼續編譯。Microsoft C and C++ 的編譯器可識別如下指令:alloc_text,
auto_inline,bss_seg,check_stack,code_seg,comment,component,conform,const_seg,data_seg,deprecated,
fenv_access,float_control,fp_contract,function,hdrstop,include_alias,init_seg,inline_depth,inline_recursion,intrinsic,
make_public,managed,message,omp,once,optimize,pack,pointers_to_members,pop_macro,push_macro,region,
endregion,runtime_checks,section,setlocale,strict_gs_check,unmanaged,vtordisp,warning。其中conform,init_seg,
pointers_to_members,vtordisp僅被C++編譯器支持。
如下是經常使用的pragma指令的詳細解釋。
1.#pragma once。保證所在文件只會被包含一次,它是基於磁盤文件的,而#ifndef則是基於宏的。
2.#pragma warning。容許有選擇性的修改編譯器的警告消息的行爲。有以下用法:
#pragma warning(disable:4507 34; once:4385; error:164) 等價於:
#pragma warning(disable:4507 34) // 不顯示4507和34號警告信息
#pragma warning(once:4385) // 4385號警告信息僅報告一次
#pragma warning(error:164) // 把164號警告信息做爲一個錯誤
#pragma warning(default:176) // 重置編譯器的176號警告行爲到默認狀態
同時這個pragma warning也支持以下格式,其中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)。
3.#pragma hdrstop。表示預編譯頭文件到此爲止,後面的頭文件不進行預編譯。BCB能夠預編譯頭文件以 加快連接的速度,但若是全部頭文件都進
行預編譯又可能佔太多磁盤空間,因此使用這個選項排除一些頭文 件。
4.#pragma message。在標準輸出設備中輸出指定文本信息而不結束程序運行。用法以下:
#pragma message("消息文本")。當編譯器遇到這條指令時就在編譯輸出窗口中將「消息文本」打印出來。
5.#pragma data_seg。通常用於DLL中,它可以設置程序中的初始化變量在obj文件中所在的數據段。若是未指定參數,初始化變量將放置在默認數
據段.data中,有以下用法:
1: #pragma data_seg("Shared") // 定義了數據段"Shared",其中有兩個變量a和b2: int a = 0; // 存儲在數據段"Shared"中3: int b; // 存儲在數據段".bss"中,由於沒有初始化4: #pragma data_seg() // 表示數據段"Shared"結束,該行代碼爲可選的
對變量進行專門的初始化是很重要的,不然編譯器將把它們放在普通的未初始化數據段中而不是放在shared中。如上述的變量b實際上是放在了未初始化數
據段.bss中。
1: #pragma data_seg("Shared")
2: int j = 0; // 存儲在數據段"Shared"中3: #pragma data_seg(push, stack1, "Shared2") //定義數據段Shared2,並將該記錄賦予別名stack1,而後放入內部編譯器棧中4: int l = 0; // 存儲在數據段"Shared2"中5: #pragma data_seg(pop, stack1) // 從內部編譯器棧中彈出記錄,直到彈出stack1,若是沒有stack1,則不作任何操做6: int m = 0; // 存儲在數據段"Shared"中,若是沒有上述pop段,則該變量將儲在數據段"Shared2"中
6.#pragma code_seg。它可以設置程序中的函數在obj文件中所在的代碼段。若是未指定參數,函數將放置在默認代碼段.text中,有以下用法:
1: void func1() { // 默認存儲在代碼段.text中
2: }3:4: #pragma code_seg(".my_data1")
5:6: void func2() { // 存儲在代碼段.my_data1中
7: }8:9: #pragma code_seg(push, r1, ".my_data2")10:11: void func3() { // 存儲在代碼段.my_data2中
12: }13:14: #pragma code_seg(pop, r1)
15:16: void func4() { // 存儲在代碼段.my_data1中
17: }
7.#pragma pack。用來改變編譯器的字節對齊方式。常規用法爲:
#pragma pack(n) //將編譯器的字節對齊方式設爲n,n的取值通常爲一、二、四、八、16,通常默認爲8
#pragma pack(show) //以警告信息的方式將當前的字節對齊方式輸出
#pragma pack(push) //將當前的字節對齊方式放入到內部編譯器棧中
#pragma pack(push,4) //將字節對齊方式4放入到內部編譯器棧中,並將當前的內存對齊方式設置爲4
#pragma pack(pop) //將內部編譯器棧頂的記錄彈出,並將其做爲當前的內存對齊方式
#pragma pack(pop,4) //將內部編譯器棧頂的記錄彈出,並將4做爲當前的內存對齊方式
#pragma pack(pop,r1) //r1爲自定義的標識符,將內部編譯器中的記錄彈出,直到彈出r1,並將r1的值做爲當前的內存對齊方式;若是r1不存在,當
不作任何操做
一個例子:
以以下結構爲例: struct {
char a;
WORD b;
DWORD c;
char d;
}
在Windows默認結構大小: sizeof(struct) = 4+4+4+4=16;
與#pragma pack(4)同樣
若設爲 #pragma pack(1), 則結構大小: sizeof(struct) = 1+2+4+1=8;
若設爲 #pragma pack(2), 則結構大小: sizeof(struct) = 2+2+4+2=10;
在#pragma pack(1)時:空間是節省了,但訪問速度下降了;
有什麼用處???
在系統通信中,如和硬件設備通訊,和其餘的操做系統進行通訊時等,必須保證雙方的一致性。
8.#pragma comment。將一個註釋記錄放置到對象文件或可執行文件中。
其格式爲:#pragma comment( comment-type [,"commentstring"] )。其中,comment-type是一個預約義的標識符,指定註釋的類型,應該是compiler,exestr,lib,linker,user之一。
compiler:放置編譯器的版本或者名字到一個對象文件,該選項是被linker忽略的。
exestr:在之後的版本將被取消。
lib:放置一個庫搜索記錄到對象文件中,這個類型應該與commentstring(指定Linker要搜索的lib的名稱和路徑)所指定的庫類型一致。在對象文件中,庫的名字跟在默認搜索記錄後面;linker搜索這個這個庫就像你在命令行輸入這個命令同樣。你能夠在一個源文件中設置多個庫搜索記錄,它們在obj
文件中出現的順序與在源文件中出現的順序同樣。
若是默認庫和附加庫的次序是須要區別的,使用/Zl編譯開關可防止默認庫放到object模塊中。
linker:指定一個鏈接選項,這樣就不用在命令行輸入或者在開發環境中設置了。只有下面的linker選項能被傳給Linker:
(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至65535;若是沒有指定ordinal,則LINK將分配一個。
NONAME關鍵字只將函數導出爲序號,沒有entryname。DATA 關鍵字指定導出項爲數據項。客戶程序中的數據項必須用extern __declspec
(dllimport)來聲明。
有三種導出定義的方法,按照建議的使用順序依次爲:
全部這三種方法能夠用在同一個程序中。LINK在生成包含導出的程序時還要建立導入庫,除非在生成過程當中使用了.exp 文件。
LINK使用標識符的修飾形式。編譯器在建立obj文件時修飾標識符。若是entryname以其未修飾的形式指定給連接器(與其在源代碼中同樣),則LINK
將試圖匹配該名稱。若是沒法找到惟一的匹配名稱,則LINK發出錯誤信息。當須要將標識符指定給連接器時,請使用Dumpbin工具獲取該標識符的修飾
名形式。
(3)/INCLUDE:symbol
/INCLUDE選項通知連接器將指定的符號添加到符號表。若要指定多個符號,請在符號名稱之間鍵入逗號(,)、分號(;)或空格。在命令行上,對每一個符號需指定一次/INCLUDE:symbol。
連接器經過將包含符號定義的對象添加到程序來解析symbol。該功能對於添加不會連接到程序的庫對象很是有用。
用該選項所指定的符號將覆蓋經過/OPT:REF對該符號進行的移除操做。
(4)/MANIFESTDEPENDENCY:manifest_dependency
/MANIFESTDEPENDENCY容許你指定位於manifest文件的<dependency>段的屬性。/MANIFESTDEPENDENCY信息能夠經過下面兩種方式傳遞給LINK:
直接在命令行運行/MANIFESTDEPENDENCY
經過#pragma comment
(5)/MERGE:from=to
/MERGE選項將第一個段(from)與第二個段(to)進行聯合,並將聯合後的段命名爲to的名稱。
若是第二個段不存在,LINK將段(from)重命名爲to的名稱。
/MERGE選項對於建立VxDs和重寫編譯器生成的段名很是有用。
(6)/SECTION:name,[[!]{DEKPRSW}][,ALIGN=#]
/SECTION選項用來改變段的屬性,當指定段所在的obj文件編譯的時候重寫段的屬性集。
可移植的可執行文件(PE)中的段(section)與新可執行文件(NE)中的節區(segment)或資源大體相同。
段(section)中包含代碼或數據。與節區(segment)不一樣的是,段(section)是沒有大小限制的連續內存塊。有些段中的代碼或數據是你的程序直接定義和
使用的,而有些數據段是連接器和庫管理器(lib.exe)建立的,而且包含了對操做系統來講很重要的信息。
/SECTION選項中的name是大小寫敏感的。
不要使用如下名稱,由於它們與標準名稱會衝突,例如,.sdata是RISC平臺使用的。
.arch
.bss
.data
.edata
.idata
.pdata
.rdata
.reloc
.rsrc
.sbss
.sdata
.srdata
.text
.xdata
爲段指定一個或多個屬性。屬性不是大小寫敏感的。對於一個段,你必須將但願它具備的屬性都進行指定;若是某個屬性未指定,則認爲是不具有這個屬
性。若是你未指定R,W或E,則已存在的讀,寫或可執行狀態將不發生改變。
要對某個屬性取否認意義,只須要在屬性前加感嘆號(!)。
E:可執行的
R:可讀取的
W:可寫的
S:對於載入該段的鏡像的全部進程是共享的
D:可廢棄的
K:不可緩存的
P:不可分頁的
注意K和P是表示否認含義的。
PE文件中的段若是沒有E,R或W屬性集,則該段是無效的。
ALIGN=#選項讓你爲一個具體的段指定對齊值。
user:放置一個常規註釋到一個對象文件中,該選項是被linker忽略的。
9.#pragma section。建立一個段。
其格式爲:#pragma section( "section-name" [, attributes] )
section-name是必選項,用於指定段的名字。該名字不能與標準段的名字想衝突。可用/SECTION查看標準段的名稱列表。
attributes是可選項,用於指定段的屬性。可用屬性以下,多個屬性間用逗號(,)隔開:
read:可讀取的
write:可寫的
execute:可執行的
shared:對於載入該段的鏡像的全部進程是共享的
nopage:不可分頁的,主要用於Win32的設備驅動程序中
nocache:不可緩存的,主要用於Win32的設備驅動程序中
discard:可廢棄的,主要用於Win32的設備驅動程序中
remove:非內存常駐的,僅用於虛擬設備驅動(VxD)中
若是未指定屬性,默認屬性爲read和write。
在建立了段以後,還要使用__declspec(allocate)將代碼或數據放入段中。
例如:
//pragma_section.cpp
#pragma section("mysec",read,write)
int j = 0;
__declspec(allocate("mysec"))
int i = 0;
int main(){}
該例中, 建立了段"mysec",設置了read,write屬性。可是j沒有放入到該段中,而是放入了默認的數據段中,由於它沒有使用__declspec(allocate)進
行聲明;而i放入了該段中,由於使用__declspec(allocate)進行了聲明。
10.#pragma push_macro與#pragma pop_macro。前者將指定的宏壓入棧中,至關於暫時存儲,以備之後使用;後者將棧頂的宏出棧,彈出的宏將覆蓋當前名稱相同的宏。例如:
1: #include <stdio.h>2: #define X 13: #define Y 24:5: int main() {
6: printf("%d",X);7: printf("\n%d",Y);8: #define Y 3 // C4005
9: #pragma push_macro("Y")
10: #pragma push_macro("X")
11: printf("\n%d",X);12: #define X 2 // C4005
13: printf("\n%d",X);14: #pragma pop_macro("X")
15: printf("\n%d",X);16: #pragma pop_macro("Y")
17: printf("\n%d",Y);18: }
輸出結果:
1
2
1
2
1
3
轉自:http://www.cnblogs.com/qinfengxiaoyue/archive/2012/06/05/2535524.html