[轉]預編譯指令#pragma的用法

[轉]預編譯指令#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.code_seg

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

3.#pragma once

  (比較經常使用)
  只要在頭文件的最開始加入這條指令就可以保證頭文件被編譯一次,這條指令實際上在VC6中就已經有了,可是考慮到兼容性並無太多的使用它。
  #pragma once是編譯相關,就是說這個編譯系統上能用,但在其餘編譯系統不必定能夠,也就是說移植性差,不過如今基本上已是每一個編譯器都有這個定義了。
  #ifndef,#define,#endif這個是C++語言相關,這是C++語言中的宏定義,經過宏定義避免文件屢次編譯。因此在全部支持C++語言的編譯器上都是有效的,若是寫的程序要跨平臺,最好使用這種方式

4.#pragma hdrstop

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

5.#pragma resource

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

6.#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 也支持以下格式:
  #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

  pragma comment(...)
  該指令將一個註釋記錄放入一個對象文件或 可執行文件 中。
  經常使用的lib關鍵字,能夠幫咱們連入一個 庫文件
  每一個編譯程序能夠用#pragma指令激活或終止該編譯程序支持的一些編譯功能。例如,對循環優化功能:
  #pragma loop_opt(on) // 激活
  #pragma loop_opt(off) // 終止
  有時,程序中會有些函數會使編譯器發出你熟知而想忽略的警告,如「Parameter xxx is never used in function xxx」,能夠這樣:
  #pragma warn —100 // Turn off the warning message for warning #100
  int insert_record(REC *r)
  { /* function body */ }
  #pragma warn +100 // Turn the warning message for warning #100 back on
  函數會產生一條有惟一特徵碼100的警告信息,如此可暫時終止該警告。
  每一個編譯器對#pragma的實現不一樣,在一個編譯器中有效在別的編譯器中幾乎無效。可從編譯器的文檔中查看。
  #pragma pack(n)和#pragma pop()
  struct sample
  {
  char a;
  double b;
  };
  當sample結構沒有加#pragma pack(n)的時候,sample按最大的成員那個對齊;
  (所謂的對齊是指對齊數爲n時,對每一個成員進行對齊,既若是成員a的大小小於n則將a擴大到n個大小;
  若是a的大小大於n則使用a的大小;)因此上面那個結構的大小爲16字節.
  當sample結構加#pragma pack(1)的時候,sizeof(sample)=9字節;無空字節。
  (另注:當n大於sample結構的最大成員的大小時,n取最大成員的大小。
  因此當n越大時,結構的速度越快,大小越大;反之則)
  #pragma pop()就是取消#pragma pack(n)的意思了,也就是說接下來的結構不用#pragma pack(n)
  #pragma comment( comment-type ,["commentstring"] )
  comment-type是一個預約義的 標識符 ,指定註釋的類型,應該是compiler,exestr,lib,linker之一。
  commentstring是一個提供爲comment-type提供附加信息的字符串。
  註釋類型:
  一、compiler:
  放置編譯器的版本或者名字到一個對象文件,該選項是被linker忽略的。
  二、exestr:
  在之後的版本將被取消。
  三、lib:
  放置一個庫搜索記錄到對象文件中,這個類型應該是和commentstring(指定你要Linker搜索的lib的名稱和路徑)這個庫的名字放在Object文件的默認庫搜索記錄的後面,linker搜索這個這個庫就像你在命令行輸入這個命令同樣。你能夠在一個源文件中設置多個庫記錄,它們在object文件中的順序和在源文件中的順序同樣。若是默認庫和附加庫的次序是須要區別的,使用Z編譯開關是防止默認庫放到object模塊。
  四、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了

8.#pragma data_seg

  #pragma data_seg介紹 [1]
  用#pragma data_seg創建一個新的數據段並定義共享數據,其具體格式爲:
  #pragma data_seg ("shareddata")
  HWND sharedwnd=NULL;//共享數據
  #pragma data_seg()
  -----------------------------------------------------------------
  1,#pragma data_seg()通常用於DLL中。也就是說,在DLL中定義一個共享的有名字的數據段。最關鍵的是:這個數據段中的全局變量能夠被多個進程共享,不然多個進程之間沒法共享DLL中的全局變量。
  2,共享數據必須初始化,不然微軟編譯器會把沒有初始化的數據放到.BSS段中,從而致使多個進程之間的共享行爲失敗。例如,
  #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,這就實現了跨進程之間的數據通訊。

編輯本段應用實例

  在網絡協議編程中,常常會處理不一樣協議的數據報文。一種方法是經過指針偏移的
  方法來獲得各類信息,但這樣作不只編程複雜,並且一旦協議有變化,程序修改起來
  也比較麻煩。在瞭解了編譯器對結構空間的分配原則以後,咱們徹底能夠利用這
  一特性定義本身的協議結構,經過訪問結構的成員來獲取各類信息。這樣作,
  不只簡化了編程,並且即便協議發生變化,咱們也只需修改協議結構的定義便可,
  其它程序無需修改,省時省力。下面以TCP協議首部爲例,說明如何定義協議結構。
  其協議結構定義以下:
  #pragma pack(1) // 按照1字節方式進行對齊
  struct TCPHEADER
  {
  short SrcPort; // 16位源端口號
  short DstPort; // 16位目的端口號
  int SerialNo; // 32位序列號
  int AckNo; // 32位確認號
  unsigned char HaderLen : 4; // 4位首部長度
  unsigned char Reserved1 : 4; // 保留6位中的4位
  unsigned char Reserved2 : 2; // 保留6位中的2位
  unsigned char URG : 1;
  unsigned char ACK : 1;
  unsigned char PSH : 1;
  unsigned char RST : 1;
  unsigned char SYN : 1;
  unsigned char FIN : 1;
  short WindowSize; // 16位窗口大小
  short TcpChkSum; // 16位TCP檢驗和
  short UrgentPointer; // 16位緊急指針
  };
  #pragma pop() // 取消1字節對齊方式
  #pragma pack規定的對齊長度,實際使用的規則是: 結構,聯合,或者類的數據成員,第一個放在偏移爲0的地方,之後每一個數據成員的對齊,按照#pragma pack指定的數值和這個數據成員自身長度中,比較大的那個進行。 可是,當#pragma pack的值等於或超過最長數據成員的長度的時候,這個值的大小將不產生任何效果。 而結構總體的對齊,則按照結構體中最大的數據成員 和 #pragma pack指定值 之間,較小的那個進行。
  指定鏈接要使用的庫好比咱們鏈接的時候用到了 WSock32.lib,你固然能夠任勞任怨地把它加入到你的工程中。可是我以爲更方便的方法是使用 #pragma 指示符,指定要鏈接的庫:#pragma comment(lib, "WSock32.lib")
相關文章
相關標籤/搜索