日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C/C++中宏使用总结

發(fā)布時間:2024/9/5 c/c++ 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C/C++中宏使用总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

http://www.cnblogs.com/areliang/archive/2007/08/02/840455.html


.C/C++中宏總結(jié)C程序的源代碼中可包括各種編譯指令,這些指令稱為預(yù)處理命令。雖然它們實(shí)際上不是C語言的一部分,但卻擴(kuò)展了C程?
序設(shè)計(jì)的環(huán)境。本節(jié)將介紹如何應(yīng)用預(yù)處理程序和注釋簡化程序開發(fā)過程,并提高程序的可讀性。?


ANSI標(biāo)準(zhǔn)定義的C語言預(yù)處理程序包括下列命令:?

#define,#error,#i?
nclude,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。非常明顯,所有預(yù)處理命令均以符號#開頭,下面分別加以介紹。

1、#define?


命令#define定義了一個標(biāo)識符及一個串。在源程序中每次遇到該標(biāo)識符時,均以定義的串代換它。ANSI標(biāo)準(zhǔn)將標(biāo)識符定義為宏名,將替換過程稱為宏?
替換。命令的一般形式為:?


#define identifier string?


注意:?


? 該語句沒有分號。在標(biāo)識符和串之間可以有任意個空格,串一旦開始,僅由一新行結(jié)束。?


? 宏名定義后,即可成為其它宏名定義中的一部分。?


? 宏替換僅僅是以文本串代替宏標(biāo)識符,前提是宏標(biāo)識符必須獨(dú)立的識別出來,否則不進(jìn)行替換。例如: #define XYZ?
this is a test,使用宏printf("XYZ");//該段不打印"this is a test"而打印"XYZ"。因?yàn)轭A(yù)編譯器識?
別出的是"XYZ"?


? 如果串長于一行,可以在該行末尾用一反斜杠' \'續(xù)行。

2、#error?


處理器命令#error強(qiáng)迫編譯程序停止編譯,主要用于程序調(diào)試。?


3、#i nclude?


命令#i nclude使編譯程序?qū)⒘硪辉次募度霂в?#xff03;i nclude的源文件,被讀入的源文件必須用雙引號或尖括號括起來。例如:?


#i nclude"stdio.h"或者#i nclude?


這兩行代碼均使用C編譯程序讀入并編譯用于處理磁盤文件庫的子程序。?


將文件嵌入#i nclude命令中的文件內(nèi)是可行的,這種方式稱為嵌套的嵌入文件,嵌套層次依賴于具體實(shí)現(xiàn)。?


如果顯式路徑名為文件標(biāo)識符的一部分,則僅在哪些子目錄中搜索被嵌入文件。否則,如果文件名用雙引號括起來,則首先檢索當(dāng)前工作目錄。如果未發(fā)現(xiàn)文件,?
則在命令行中說明的所有目錄中搜索。如果仍未發(fā)現(xiàn)文件,則搜索實(shí)現(xiàn)時定義的標(biāo)準(zhǔn)目錄。?


如果沒有顯式路徑名且文件名被尖括號括起來,則首先在編譯命令行中的目錄內(nèi)檢索。?


如果文件沒找到,則檢索標(biāo)準(zhǔn)目錄,不檢索當(dāng)前工作目錄。

4、條件編譯命令?


有幾個命令可對程序源代碼的各部分有選擇地進(jìn)行編譯,該過程稱為條件編譯。商業(yè)軟件公司廣泛應(yīng)用條件編譯來提供和維護(hù)某一程序的許多顧客版本。?


#if、#else,#elif及#endif?


#if的一般含義是如果#if后面的常量表達(dá)式為true,則編譯它與#endif之間的代碼,否則跳過這些代碼。命令#endif標(biāo)識一個#if塊的?
結(jié)束。?


#if constant-expression?


statement sequence?


#endif?


跟在#if后面的表達(dá)式在編譯時求值,因此它必須僅含常量及已定義過的標(biāo)識符,不可使用變量。表達(dá)式不許含有操作符sizeof(sizeof也是編譯?
時求值)。?


#else命令的功能有點(diǎn)象C語言中的else;#else建立另一選擇(在#if失敗的情況下)。?


注意,# else屬于# if塊。?


#elif命令意義與ELSE IF 相同,它形成一個if else-if階梯狀語句,可進(jìn)行多種編譯選擇。?


#elif 后跟一個常量表達(dá)式。如果表達(dá)式為true,則編譯其后的代碼塊,不對其它#elif表達(dá)式進(jìn)行測試。否則,順序測試下一塊。?


#if expression?


statement sequence?


#elif expression1?


statement sequence?


#endif?


在嵌套的條件編譯中#endif、#else或#elif與最近#if或#elif匹配。?


# ifdef 和# ifndef?


條件編譯的另一種方法是用#ifdef與#ifndef命令,它們分別表示"如果有定義"及"如果無定義"。?


# ifdef的一般形式是:?


# ifdef macroname?


statement sequence?


#endif?


#ifdef與#ifndef可以用于#if、#else,#elif語句中,但必須與一個#endif。

5、#undef?


命令#undef 取消其后那個前面已定義過有宏名定義。一般形式為:?


#undef macroname

6、#line?


命令# line改變__LINE__與__FILE__的內(nèi)容,它們是在編譯程序中預(yù)先定義的標(biāo)識符。命令的基本形式如下:?


# line number["filename"]?


其中的數(shù)字為任何正整數(shù),可選的文件名為任意有效文件標(biāo)識符。行號為源程序中當(dāng)前行號,文件名為源文件的名字。命令# line主要用于調(diào)試及其它特殊?
應(yīng)用。?


注意:在#line后面的數(shù)字標(biāo)識從下一行開始的數(shù)字標(biāo)識。

7、預(yù)定義的宏名?


ANSI標(biāo)準(zhǔn)說明了C中的五個預(yù)定義的宏名。它們是:?


__LINE__?


__FILE__?


__DATE__?


__TIME__?


__STDC__?


如果編譯不是標(biāo)準(zhǔn)的,則可能僅支持以上宏名中的幾個,或根本不支持。記住編譯程序也許還提供其它預(yù)定義的宏名。?


__LINE__及__FILE__宏指令在有關(guān)# line的部分中已討論,這里討論其余的宏名。?


__DATE__宏指令含有形式為月/日/年的串,表示源文件被翻譯到代碼時的日期。?


源代碼翻譯到目標(biāo)代碼的時間作為串包含在__TIME__中。串形式為時:分:秒。?


如果實(shí)現(xiàn)是標(biāo)準(zhǔn)的,則宏__STDC__含有十進(jìn)制常量1。如果它含有任何其它數(shù),則實(shí)現(xiàn)是非標(biāo)準(zhǔn)的。編譯C++程序時,編譯器自動定義了一個預(yù)處理名?
字__cplusplus,而編譯標(biāo)準(zhǔn)C時,自動定義名字__STDC__。?


注意:宏名的書寫由標(biāo)識符與兩邊各二條下劃線構(gòu)成。?


(部分內(nèi)容出自:http://www.bc-cn.net/Article/kfyy/cyy/jc/200511/919.html)

8、C、C++宏體中出現(xiàn)的#,#@,##?


宏體中,#的功能是將其后面的宏參數(shù)進(jìn)行字符串化操作(Stringfication),簡單說就是在對它所引用的宏變量通過替換后在其左右各加上一個?
雙引號。?


而##被稱為連接符(concatenator),用來將兩個Token連接為一個Token。注意這里連接的對象是Token就行,而不一定是宏的變?
量。比如你要做一個菜單項(xiàng)命令名和函數(shù)指針組成的結(jié)構(gòu)體的數(shù)組,并且希望在函數(shù)名和菜單項(xiàng)命令名之間有直觀的、名字上的關(guān)系。那就可以使用:宏參數(shù)##?
固定部分。當(dāng)然還可以n個##符號連接 n+1個Token,這個特性也是#符號所不具備的。?


#@的功能是將其后面的宏參數(shù)進(jìn)行字符化。

9、C宏中的變參...?


...在C宏中稱為Variadic Macro,也就是變參宏。比如:?


#define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)?


或者#define myprintf(templt,args...) fprintf(stderr,templt,args)?


第一個宏中由于沒有對變參起名,我們用默認(rèn)的宏__VA_ARGS__來替代它。第二個宏中,我們顯式地命名變參為args,那么我們在宏定義中就可以?
用args來代指變參了。同C語言的stdcall一樣,變參必須作為參數(shù)表的最后有一項(xiàng)出現(xiàn)。當(dāng)上面的宏中我們只能提供第一個參數(shù)templt時,C?
標(biāo)準(zhǔn)要求我們必須寫成: myprintf(templt,);的形式。這時的替換過程為:myprintf("Error!\n",);替換為:?
fprintf(stderr,"Error!\n",).?


這是一個語法錯誤,不能正常編譯。這個問題一般有兩個解決方法。首先,GNU CPP提供的解決方法允許上面的宏調(diào)用寫成:?
myprintf(templt);而它將會被通過替換變成: fprintf(stderr,"Error!\n",);?


很明顯,這里仍然會產(chǎn)生編譯錯誤(非本例的某些情況下不會產(chǎn)生編譯錯誤)。除了這種方式外,c99和GNU CPP都支持下面的宏定義方式:?


#define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)?


這時,##這個連接符號充當(dāng)?shù)淖饔镁褪钱?dāng)__VAR_ARGS__為空的時候,消除前面的那個逗號。那么此時的翻譯過程如下:?
myprintf(templt);被轉(zhuǎn)化為: fprintf(stderr,templt);?


這樣如果templt合法,將不會產(chǎn)生編譯錯誤。

10、#pragma的使用【轉(zhuǎn)載】?


在所有的預(yù)處理指令中,#Pragma 指令可能是最復(fù)雜的了,它的作用是設(shè)定編譯器的狀態(tài)或者是指示編譯器完成一些特定的動作。#pragma指令對?
每個編譯器給出了一個方法,在保持與C和C ++語言完全兼容的情況下,給出主機(jī)或操作系統(tǒng)專有的特征。依據(jù)定義,編譯指示是機(jī)器或操作系統(tǒng)專有的,且?
對于每個編譯器都是不同的。?


其格式一般為: #Pragma Para,其中Para 為參數(shù),下面來看一些常用的參數(shù)。?


(1)message 參數(shù)。 Message 參數(shù)是我最喜歡的一個參數(shù),它能夠在編譯信息輸出窗口中輸出相應(yīng)的信息,這對于源代碼信息的控制是非常?
重要的。其使用方法為:?


#Pragma message("消息文本")?


當(dāng)編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。?


當(dāng)我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設(shè)置這些宏,此時我們可以用這條指令在編譯的時候就進(jìn)行檢查。?
假設(shè)我們希望判斷自己有沒有在源代碼的什么地方定義了_X86這個宏可以用下面的方法?


#ifdef _X86?


#Pragma message("_X86 macro activated!")?


#endif?


當(dāng)我們定義了_X86這個宏以后,應(yīng)用程序在編譯時就會在編譯輸出窗口里顯示"_?


X86 macro activated!"。我們就不會因?yàn)椴挥浀米约憾x的一些特定的宏而抓耳撓腮了。?


(2)另一個使用得比較多的pragma參數(shù)是code_seg。格式如:?


#pragma code_seg( ["section-name"[,"section-class"] ] )?


它能夠設(shè)置程序中函數(shù)代碼存放的代碼段,當(dāng)我們開發(fā)驅(qū)動程序的時候就會使用到它。?


(3)#pragma once (比較常用)?


只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實(shí)際上在VC6中就已經(jīng)有了,但是考慮到兼容性并沒有太多的使用它。?


(4)#pragma hdrstop表示預(yù)編譯頭文件到此為止,后面的頭文件不進(jìn)行預(yù)編譯。BCB可以預(yù)編譯頭文件以加快鏈接的速度,但如果所有頭文?
件都進(jìn)行預(yù)編譯又可能占太多磁盤空間,所以使用這個選項(xiàng)排除一些頭文件。?


有時單元之間有依賴關(guān)系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragma startup指定編譯優(yōu)先級,如果使用了?
#pragma package(smart_init) ,BCB就會根據(jù)優(yōu)先級的大小先后編譯。?


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


(6)#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 )保存所有警告信息的現(xiàn)有的警告狀態(tài)。?


#pragma warning( push, n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告等級設(shè)定為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(...)?


該指令將一個注釋記錄放入一個對象文件或可執(zhí)行文件中。?


常用的lib關(guān)鍵字,可以幫我們連入一個庫文件。?


(8)用pragma導(dǎo)出dll中的函數(shù)?


傳統(tǒng)的到出 DLL 函數(shù)的方法是使用模塊定義文件 (.def),Visual C++ 提供了更簡潔方便的方法,那就?
是"__declspec()"關(guān)鍵字后面跟"dllexport",告訴連接去要導(dǎo)出這個函數(shù),例如:?


__declspec(dllexport) int __stdcall MyExportFunction(int iTest);?


把"__declspec(dllexport)"放在函數(shù)聲明的最前面,連接生成的 DLL 就會導(dǎo)出函?
數(shù)"_MyExportFunction@4"。?


上面的導(dǎo)出函數(shù)的名稱也許不是我的希望的,我們希望導(dǎo)出的是原版的"MyExportFunction"。還好,VC 提供了一個預(yù)處理指示?
符"#pragma"來指定連接選項(xiàng) (不僅僅是這一個功能,還有很多指示功能) ,如下:?


#pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")?


這下就天如人愿了:)。如果你想指定導(dǎo)出的順序,或者只將函數(shù)導(dǎo)出為序號,沒有 Entryname,這個預(yù)處理指示符 (確切地說是連接器) 都能夠?
實(shí)現(xiàn),看看 MSDN 的語法說明:?


/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]?


@ordinal 指定順序;NONAME 指定只將函數(shù)導(dǎo)出為序號;DATA 關(guān)鍵字指定導(dǎo)出項(xiàng)為數(shù)據(jù)項(xiàng)。?


⑨每個編譯程序可以用#pragma指令激活或終止該編譯程序支持的一些編譯功能。例如,對循環(huán)優(yōu)化功能:?


#pragma loop_opt(on) // 激活?


#pragma loop_opt(off) // 終止?


有時,程序中會有些函數(shù)會使編譯器發(fā)出你熟知而想忽略的警告,如"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?


函數(shù)會產(chǎn)生一條有唯一特征碼100的警告信息,如此可暫時終止該警告。?


每個編譯器對#pragma的實(shí)現(xiàn)不同,在一個編譯器中有效在別的編譯器中幾乎無效。可從編譯器的文檔中查看。
⑩#pragm pack()的使用?


#pragma pack規(guī)定的對齊長度,實(shí)際使用的規(guī)則是:?


? 結(jié)構(gòu),聯(lián)合,或者類的數(shù)據(jù)成員,第一個放在偏移為0的地方,以后每個數(shù)據(jù)成員的對齊,按照#pragma pack指定的數(shù)值和這?
個數(shù)據(jù)成員自身長度中,比較小的那個進(jìn)行。?


? 也就是說,當(dāng)#pragma pack的值等于或超過所有數(shù)據(jù)成員長度的時候,這個值的大小將不產(chǎn)生任何效果。?


? 而結(jié)構(gòu)整體的對齊,則按照結(jié)構(gòu)體中最大的數(shù)據(jù)成員 和 #pragma pack指定值之間,較小的那個進(jìn)行。?


注意:文件使用#pragma pack(n) 改變了缺省設(shè)置而不恢復(fù),通常可以使用#pragma pack(push, n)和#pragma?
pack(pop)進(jìn)行設(shè)置與恢復(fù)。?


注:關(guān)于宏函數(shù)的內(nèi)容在另外的專題。關(guān)于宏使用的誤區(qū)在描述宏的時候已經(jīng)在文中提到了,最后再給出一個例子,描述的Side Effect是指宏在展開?
的時候?qū)ζ鋮?shù)可能進(jìn)行多次Evaluation(也就是取值)對程序造成的錯誤影響。?


假設(shè)在一個系統(tǒng)中,有一個32b的寄存器(REG)保存狀態(tài),其中高16b表示一種含義,低16b表示另一種含義(這在程序中經(jīng)常出現(xiàn))。現(xiàn)在要把高低?
16b分開,不考慮實(shí)際中的特殊要求,將代碼寫成:?


#define High16bit(REG) (REG>>16)?


#define Low16bit(REG) ((REG<<16)>>16)?


對于這種寫法完成的功能在大多數(shù)情況是足夠了,這里不討論。主要談?wù)撨@種寫法的負(fù)面影響,如果在程序中分別在不同的語句中使用High16bit和?
Low16bit,那么就可能那就是Side effect,特別寄存器REG是狀態(tài)寄存器,他的狀態(tài)可能隨時變化,那么引起的問題就是高低16b根本?
取的不是同一個時刻狀態(tài)寄存器。這種錯誤在程序中找出就比較難了。在這里我把條件弱化了,試想在一個宏體中,如果對參數(shù)多次取值也是可能引起問題,那就 更難了。

總結(jié)

以上是生活随笔為你收集整理的C/C++中宏使用总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。