1. 程式人生 > >使用巨集定義對字串進行處理#pragma

使用巨集定義對字串進行處理#pragma

在巨集定義的使用中有一個字串化運算子即 “#”運算子,出現在巨集定義之後的“#”運算子會把跟在其後得到引數轉化為一個字串,優勢也稱這種用法的“#”稱之為字串化 運算子。舉例如下:

#include <iostream>
#define PASTE(n)  "abcdefkj"#n
//C語言中的巨集定義
//使用巨集定義對字串進行處理
using namespace std;


int main()
{
    int n = 16;

    printf("PASTE  = %s",PASTE(n));
    system("pause");      
    return
0; }
#pragma once        //意思是隻包含一次
//#pragma once <==>  #ifndef    #define  #endif

class myTeacher // ctrl + shift +u  將字串變大寫
{
public:
    myTeacher(void);

};

並接運算子

並接運算子即“##”運算子,”##”運算子用於把引數連線到一起,預處理程式把出現在“##”運算子兩側的引數合併成一個符號;
如下:

#include <iostream>
#define NUM(a,b,c)  a##b##c
#define STR(a,b,c) a##b##c
//C語言中的巨集定義 //使用巨集定義對字串進行處理 using namespace std; int main() { printf("PASTE = %d\n",NUM(1,2,3)); printf("PASTE = %s\n",STR("aa","bb","cc"));; system("pause"); return 0; }
#include <iostream>

//C語言中的巨集定義
//使用巨集定義對字串進行處理

using namespace std;
#define CONFIG 0

#if defined CONFIG   //#if defined <==> #ifdef
void f() { printf("ni hao!\n"); } #endif int main() { f(); system("pause"); return 0; }
   #ifndef <==> #if !defined
#error token  //用來輸出錯誤資訊 token
#pragma token   //此指令的作用是觸發相應的動作,如果token存在,組觸發相應的動作,如果不存在則忽略,一般作為編譯系統使用,例如使用#pragma once,防止同一程式碼被多次的包含
message 引數
Message 引數能夠在編譯資訊輸出視窗中輸出相應的資訊,這對於原始碼資訊的控制是非常重要的。其使用方法為:
#pragma message("訊息文字")
當編譯器遇到這條指令時就在編譯輸出視窗中將訊息文字打印出來。
當我們在程式中定義了許多巨集來控制原始碼版本的時候,我們自己有可能都會忘記有沒有正確的設定這些巨集,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在原始碼的什麼地方定義了_X86這個巨集可以用下面的方法
#ifdef _X86
#pragma message("_X86 macro activated!")
#endif
當我們定義了_X86這個巨集以後,應用程式在編譯時就會在編譯輸出窗口裡顯示“_X86 macro activated! ”。我們就不會因為不記得自己定義的一些特定的巨集而抓耳撓腮了。
code_seg
另一個使用得比較多的pragma引數是code_seg。格式如:
#pragma code_seg(["section-name"[,"section-class"]])
它能夠設定程式中函式程式碼存放的程式碼段,當我們開發驅動程式的時候就會使用到它。
#pragma once
(比較常用)
只要在標頭檔案的最開始加入這條指令就能夠保證標頭檔案被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到相容性並沒有太多的使用它。
#pragma once是編譯相關,就是說這個編譯系統上能用,但在其他編譯系統不一定可以,也就是說移植性差,不過現在基本上已經是每個編譯器都有這個定義了。
#ifndef,#define,#endif這個是C++語言相關,這是C++語言中的巨集定義,通過巨集定義避免檔案多次編譯。所以在所有支援C++語言的編譯器上都是有效的,如果寫的程式要跨平臺,最好使用這種方式
#pragma hdrstop
#pragma hdrstop表示預編譯標頭檔案到此為止,後面的標頭檔案不進行預編譯。BCB可以預編譯標頭檔案以加快連結的速度,但如果所有標頭檔案都進行預編譯又可能佔太多磁碟空間,所以使用這個選項排除一些標頭檔案。
有時單元之間有依賴關係,比如單元A依賴單元B,所以單元B要先於單元A編譯。你可以用#pragma startup指定編譯優先順序,如果使用了#pragma package(smart_init) ,BCB就會根據優先順序的大小先後編譯。
#pragma resource
#pragma resource "*.dfm"表示把*.dfm檔案中的資源加入工程。*.dfm中包括窗體外觀的定義。
#pragma warning
#pragma warning(disable:450734;once:4385;error:164)
等價於:
#pragma warning(disable:450734)//不顯示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)
在這段程式碼的最後,重新儲存所有的警告資訊(包括470547064707)。
#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//Turnoffthewarningmessageforwarning#100
intinsert_record(REC*r)
{/*functionbody*/}
#pragma warn+100//Turnthewarningmessageforwarning#100backon
函式會產生一條有唯一特徵碼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 apop()就是取消#pragma pack(n)的意思了,也就是說接下來的結構不用#pragma pack(n)
#pragma comment(comment-type,["commentstring"])
comment-type是一個預定義的識別符號,指定註釋的型別,應該是compiler,exestr,lib,linker之一。
comment string是一個提供為comment-type提供附加資訊的字串。
註釋型別:
1、compiler:
放置編譯器的版本或者名字到一個物件檔案,該選項是被linker忽略的。
2、exestr:
在以後的版本將被取消。
3、lib:
放置一個庫搜尋記錄到物件檔案中,這個型別應該是和comment string(指定你要Linker搜尋的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 在匯出表中指定範圍在 165,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 disable

  在函式前宣告,只對一個函式有效。該函式呼叫過程中將不可被中斷。一般在C51中使用較多。
#pragma data_seg
介紹
用#pragma data_seg建立一個新的資料段並定義共享資料,其具體格式為:
#pragma data_seg("shareddata")
HWNDsharedwnd=NULL;//共享資料
#pragma data_seg()
-----------------------------------------------------------------
1#pragma data_seg()一般用於DLL中。也就是說,在DLL中定義一個共享的有名字的資料段。最關鍵的是:這個資料段中的全域性變數可以被多個程序共享,否則多個程序之間無法共享DLL中的全域性變數。
2,共享資料必須初始化,否則微軟編譯器會把沒有初始化的資料放到.BSS段中,從而導致多個程序之間的共享行為失敗。例如,
#pragma data_seg("MyData")
intg_Value;//Notethattheglobalisnotinitialized.
#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 region
#pragma region是Visual C++中特有的預處理指令。它可以讓你摺疊特定的程式碼塊,從而使介面更加清潔,便於編輯其他程式碼。摺疊後的程式碼塊不會影響編譯。你也可以隨時展開程式碼塊以進行編輯等操作。
格式:
1
#pragma region name#pragma endregion comment
使用示例如下:
#pragma region Variables
HWND hWnd; 
const size_t Max_Length = 20;

//other variables

#pragma endregion This region contains global variables.
如上邊所示,需要摺疊的程式碼必須包含在#pragma region和#pragma endregion之間。#pragma region和#pragma endregion之後可以新增一些用來註釋的文字。當你摺疊程式碼塊後,這些文字會顯示在摺疊的位置。
摺疊程式碼塊的方法:如同Visual C++中摺疊函式、類、名稱空間,當代碼被包含在如上所述的指令之間後,#pragma region這一行的左邊會出現一個“-”號,單擊以摺疊內容,同時“-”號會變成“+”號,再次單擊可以展開程式碼塊。
此預編譯指令在Visual Studio 2005及以上版本可以使用。但是在Visual Studio 2005中,當#pragma region之後包含類似“1st”這類的文字,會導致“error C2059: syntax error : 'bad suffix on number'”的編譯錯誤。避免使用數字或者將數字與字母分離可以解決這個問題。[1] 
應用例項(#pragma pack)編輯
在網路協議程式設計中,經常會處理不同協議的資料報文。一種方法是通過指標偏移的方法來得到各種資訊,但這樣做不僅程式設計複雜,而且一旦協議有變化,程式修改起來也比較麻煩。在瞭解了編譯器對結構空間的分配原則之後,我們完全可以利用這一特性定義自己的協議結構,通過訪問結構的成員來獲取各種資訊。這樣做,不僅簡化了程式設計,而且即使協議發生變化,我們也只需修改協議結構的定義即可,其它程式無需修改,省時省力。下面以TCP協議首部為例,說明如何定義協議結構。
其協議結構定義如下:
#pragma pack(1)//按照1位元組方式進行對齊
struct TCPHEADER
{
shortSrcPort;//16位源埠號
shortDstPort;//16位目的埠號
intSerialNo;//32位序列號
intAckNo;//32位確認號
unsignedcharHaderLen:4;//4位首部長度
unsignedcharReserved1:4;//保留16位中的4位
unsignedcharReserved2:2;//保留16位中的2位
unsignedcharURG:1;
unsignedcharACK:1;
unsignedcharPSH:1;
unsignedcharRST:1;
unsignedcharSYN:1;
unsignedcharFIN:1;
shortWindowSize;//16位視窗大小
shortTcpChkSum;//16位TCP檢驗和
shortUrgentPointer;//16位緊急指標
};
#pragm apop()//取消1位元組對齊方式
#pragma pack規定的對齊長度,實際使用的規則是: 結構,聯合,或者類的資料成員,第一個放在偏移為0的地方,以後每個資料成員的對齊,按照#pragma pack指定的數值和這個資料成員自身長度中,比較小的那個進行。 但是,當#pragma pack的值等於或超過最長資料成員的長度的時候,這個值的大小將不產生任何效果。 而結構整體的對齊,則按照結構體中最大的資料成員進行。