解析#pragma指令
在所有的預處理指令中,#Pragma 指令可能是最複雜的了,它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C++語言完全兼容的情況下,給出主機或操作系統專有的特徵。依據定義,編譯指示是機器或操作系統專有的,且對於每個編譯器都是不同的。
其格式一般爲:#Pragma Para
其中Para 爲參數,下面來看一些常用的參數。
message 參數。
它能夠在編譯信息輸出窗口中輸出相應的信息,這對於源代碼信息的控制是非常重要的。其使用方法爲: #Pragma message(“消息文本”)
當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。 當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什麼地方定義了_X86這個宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
當我們定義了_X86這個宏以後,應用程序在編譯時就會在編譯輸出窗口裏顯示“_ X86 macro activated!”。我們就不會因爲不記得自己定義的一些特定的宏而抓耳撓腮了 。
註釋:
#error也可以用於輸出編譯信息,信息跟在#error後面。但它會引發編譯錯誤。
code_seg。
格式如: #pragma code_seg( ["section-name"[,"section-class"] ] )
它能夠設置程序中函數代碼存放的代碼段,當我們開發驅動程序的時候就會使用到它。
#pragma once (比較常用)
只要在頭文件的最開始加入這條指令就能夠保證頭文件只被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到兼容性並沒有太多的使用它。
#pragma hdrstop
表示預編譯頭文件到此爲止,後面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能佔太多磁盤空間,所以使用這個選項排除一些頭文件。有時單元之間有依賴關係,比如單元A依賴單元B,所以單元B要先於單元A編譯。你可以用#pragma startup指定編譯優先級,如果使用了#pragma package(smart_init) ,BCB就會根據優先級的大小先後編譯。
#pragma resource "*.dfm"
表示把*.dfm文件中的資源加入工程。*.dfm中包括窗體外觀的定義。
#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)。
pragma comment(...)
該指令將一個註釋記錄放入一個對象文件或可執行文件中。常用的lib關鍵字,可以幫我們連入一個庫文件。
#pragma pack()
我們知道在VC中,對於想結構體Struct這樣的類型,VC採用8字節對齊的方式,如果我們不想使用8字節對齊(在網絡變成中經常需要這樣),我們可以在結構體前面加上
#pragma pack(1)
struct
{
......
}
#pragma pack( )
#ifndef和#pragma once的區別
爲了避免同一個文件被include多次,C/C++中有兩種方式,一種是#ifndef方式,一種是#pragma once方式。在能夠支持這兩種方式的編譯器上,二者並沒有太大的區別,但是兩者仍然還是有一些細微的區別。
方式一:
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
... ... // 聲明、定義語句
#endif
方式二:
... ... // 聲明、定義語句
#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的編譯器報錯,所以混用兩種方法似乎不能帶來更多的好處,倒是會讓一些不熟悉的人感到困惑。
選擇哪種方式,應該在瞭解兩種方式的情況下,視具體情況而定。只要有一個合理的約定來避開缺點,我認爲哪種方式都是可以接受的。而這個已經不是標準或者編譯器的責任了,應當由程序員自己或者小範圍內的開發規範來搞定。