C語言的預處理程序_第1頁
C語言的預處理程序_第2頁
C語言的預處理程序_第3頁
C語言的預處理程序_第4頁
已閱讀5頁,還剩64頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1C語言的預處理程序ANS!標準定義的C語言預處理程序包括下列命令:#definetterrorttinclude#if#else#elifttendif#ifdefSifndefSundef#lineSpragma非常明顯,所有預處理命令均以符號#開頭,下面分別加以介紹。2#define命令#define定義了一個標識符及ー個串。在源程序中每次遇到該標識符時,均以定義的串代換它。ANSI標準將標識符定義為宏名,將替換過程稱為宏替換。命令的一般形式為:#defineidentifierstring注意,該語句沒有分號。在標識符和串之間可以有任意個空格,串一旦開始,僅由一新行結束。例如,如希望TURE取值1,FALSE取值〇,可說明兩個宏#define#defineTURE1#defineFALSE0這使得在源程序中每次遇到TURE或FALSE就用0或1代替。例如,在屏幕上打印‘‘012”:printfC%d%d%d",FALSE,TRUE,TRUE+1);宏名定義后,即可成為其它宏名定義中的一部分。例如,下面代碼定義了ONE、TWO及THREE的值。SdefineONE1SdefineTWOONE+ONE#defineTHREEONE+TWO懂得宏替換僅僅是以串代替標識符這點很重要。因此,如果希望定義ー個標準錯誤信息,可編寫如下代碼:#defineE_MS,zstandarderroroninput\n"printf(E_MS);編譯程序遇到標識符E_MS時,就用"standarderroroninput\n”替換。對于編譯程序,printfO語句實際是如下形式:printf("standarderroroninput\n;")如果在串中含有標識符,則不進行替換。例如:#defineXYZthisisatestprintf("XYZ");該段不打印"thisisatest"而打印"XYZ"。如果串長于一行,可以在該行末尾用一反斜杠續(xù)行,例如:#defineLONG_STRING*thisisaverylong\stringthatisusedasanexampleC語言程序普遍使用大寫字母定義標識符。這種約定可使人讀程序時很快發(fā)現(xiàn)哪里有宏替換。最好是將所有的#define放到文件的開始處或獨立的文件中(用#include訪問),而不是將它們分散到整個程序中。宏代換的最一般用途是定義常量的名字和程序中的“游戲數(shù)”。例如,某ー程序定義了一個數(shù)組,而它的兒個子程序要訪問該數(shù)組,不應直接以常量定數(shù)組大小,最好是用名字定義之(需改變數(shù)組大小時)。#defineMAX_SIZE100floatbalance[MAXSIZE;]#define命令的另ー個有用特性是,宏名可以取參量。每次遇到宏名時,與之相連的形參均由程序中的實參代替。例如:當編譯該程序時,由MlN(a,b)定義的表達式被替換,x和y用作操作數(shù),即printf()語句被代換后取如下形式:printf("theminimumis:%,d"(xくy)?x:y);用宏代換代替實在的函數(shù)的一大好處是宏替換增加了代碼的速度,因為不存在函數(shù)調用的開銷。但增加速度也有代價:由于重復編碼而增加了程序長度。3#error處理器命令#error強迫編譯程序停止編譯,主要用于程序調試。4#include命令#include使編譯程序將另一源文件嵌入帶有#include的源文件,被讀入的源文件必須用雙引號或尖括號括起來。例如:#include"stdio.h"#includeくstdio.h>這兩行代碼均使用C編譯程序讀入并編譯用于處理磁盤文件庫的子程序。將文件嵌入#include命令中的文件內是可行的,這種方式稱為嵌套的嵌入文件,嵌套層次依賴于具體實現(xiàn)。如果顯式路徑名為文件標識符的一部分,則僅在哪些子目錄中搜索被嵌入文件。否則,如果文件名用雙引號括起來,則首先檢索當前工作目錄。如果未發(fā)現(xiàn)文件,則在命令行中說明的所有目錄中搜索。如果仍未發(fā)現(xiàn)文件,則搜索實現(xiàn)時定義的標準目錄。如果沒有顯式路徑名旦文件名被尖括號括起來,則首先在編譯命令行中的目錄內檢索。如果文件沒找到,則檢索標準目錄,不檢索當前工作目錄。5條件編譯命令有幾個命令可對程序源代碼的各部分有選擇地進行編譯,該過程稱為條件編譯。商業(yè)軟件公司廣泛應用條件編譯來提供和維護某ー程序的許多顧客版本。1.#if>#else,#elif及#endif#if的一般含義是如果#if后面的常量表達式為true,則編譯它與#endif之間的代碼,否則跳過這些代碼。命令#endif標識ー個#if塊的結束,參見例4-130#ifconstant-expressionstatementsequence#endif由于MAX大于99,以上程序在屏幕上顯示一串消息。該例說明了一個重點:跟在#if后面的表達式在編譯時求值,因此它必須僅含常量及已定義過的標識符,不可使用變量。表達式不許含有操作符sizeof。#else命令的功能有點象C語言中的else;#else建立另ー選擇(在#if失敗的情況下)。因而上面的例子可擴充,參見例4T4。在此例中,因為MAX小于99,所以,不編譯#if塊,而是編譯#else塊,因此,屏幕上顯示‘'compiledforsmallarray?消息。注意,#else既是#if塊又是#else塊頭。這是因為任何#if僅有一個Sendif〇#elif命令意義與ELSEIF相同,它形成一個ifelse-if階梯狀語句,可進行多種編譯選擇。#elif后跟ー個常量表達式。如果表達式為true,則編譯其后的代碼塊,不對其它#elif表達式進行測迖。否則,順序測試下ー塊。#ifexpressionstatementsequence#elifexpressionlstatementsequence#elifexpression2statementsequence#elifexpressionsstatementsequenceelifexpression4elifexpression3Nstatementsequence#endif例如:下面程序利用ACTIVe_COUNTRY定義貨幣符號。defineUS0defineENGLAND1defineFRANCE2defineACTIVE_COUNTRYUSifACTIVE_COUNTRY==UScharcurrency[]-dollar;elifACTIVE_COUNTRY==ENGLANDcharcurrency[]="pound;"#elsecharcurrency[]=*franc;"Sendifif與#elif命令可能一直嵌套到實現(xiàn)規(guī)定的權限,其中#endif、#else或#elif與最近#if或#elif關聯(lián)。例如,下面程序是完全有效的。#ifMAX>100#ifSERIAL_VERSIONintport=198;#elifintport=200;#elif#elsecharoutbuffer[100];#endif2.#ifdef和#ifndef條件編譯的另一種方法是用#ifdef與#ifndef命令,它們分別表示“如果有定義”及“如果無定義”。ifdef的一?般形式是:ifdefmacronamestatementsequencettendif如果宏名在前面#define語句中已定義過,則該語句后的代碼塊被編譯。ifndef的一般形式是:#ifndefmacronamestatementsequencettendif如果宏名在#define語句中無定義,則編譯該代碼塊。ttifde!與#ifndef可以用于#else語句中,但#elif不行。參見4-15〇上述代碼打印“HiTed”及“RALPHnotdefinedw〇如果TED沒有定義,則顯示“Hiany〇ne",后面是"RALPHnotdefinedM〇可以像嵌套#if那樣將#ifdef與#ifndef嵌套至任意深度。#undef命令#undef取消其后那個前面已定義過有宏名定義。一般形式為:#undefmacroname例如:defineLEN100difineWIDTH100chararray[LEN][WIDTH];undefLENundefWIDTH*atthispointbothLENandWIDTHareundefined*/直到遇到#undef語句之前,LEN與WIDTH均有定義。undef的主要目的是將宏名局限在僅需要它們的代碼段中。#Iine命令#line改變_LINE一與一FILE一的內容,它們是在編譯程序中預先定義的標識符。命令的基本形式如下:#linenumber[/zfi1ename"]其中的數(shù)字為任何正整數(shù),可選的文件名為任意有效文件標識符。行號為源程序中當前行號,文件名為源文件的名字。命令#line主要用于調試及其它特殊應用。例如,下面說明行計數(shù)從1〇〇開始;printf()語句顯示數(shù)102I因為它是語句#line100后的第3行。#line100/?初始化行計數(shù)器*/main()/*行號100*/{/?行號101*/printf(/z%d\n/z,_line_);/?行號102*/)Spragma命令#pragma為實現(xiàn)時定義的命令,它允許向編譯程序傳送各種指令。例如,編譯程序可能有一ー種選擇,它支持對程序執(zhí)行的跟蹤??捎?pragma語句指定一個跟蹤選擇。在所有的預處理指令中,Spragma指令可能是最復雜的了,它的作用是設定編譯器的狀態(tài)或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和生語言完全兼容的情況下,給出主機或操作系統(tǒng)專有的特征。依據(jù)定義,編譯指示是機器或操作系統(tǒng)專有的,且對于每個編譯器都是不同的。其格式一般為:Spragmapara其中para為參數(shù),下面來看?些常用的參數(shù)。(1)message參數(shù)message參數(shù)是我最喜歡的ー個參數(shù),它能夠在編譯信息輸出窗口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:#pragmamessage("消息文本”)當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢査。假設我們希望判斷自己有沒有在源代碼的什么地方定義了一X86這個宏,可以用下面的方法:#ifdef_X86ttpragmamessage(ZZ_X86macroactivated!zz)#endif我們定義了一X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯不86macroactivated!zzo我們就不會因為不記得自己定義的ー些特定的宏而抓耳撓腮了。(2)另--個使用得比較多的pragma參數(shù)是code_seg格式如:Spragmacode_seg(["section-name”[,"section-class"]])它能夠設置程序中函數(shù)代碼存放的代碼段,當我們開發(fā)驅動程序的時候就會使用到它。(3)#pragmaonce仕匕較常用)只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經(jīng)有了,但是考慮到兼容性并沒有太多的使用它。⑷#pragmahdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除一些頭文件。有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragmastartup指定編譯優(yōu)先級,如果使用了#pragmapackage(smart_init),BCB就會根據(jù)優(yōu)先級的大小先后編譯。(5)#pragmaresource dfm"表示把?.dfm文件中的資源加入工程。*.dfm中包括窗體外觀的定義。(6)ftpragmawarning(disable:450734;once:4385;error:164)等價于:#pragmawarning(disable:450734) //不顯示4507和34號警告信息Spragmawarning(once:4385) //4385號警告信息僅報告ー次ttpragmawarning(error:164) 〃把!64號警告信息作為一1個錯誤。同時這個pragmawarning也支持如下格式:#pragmawarning(push[,n])Spragmawarning(pop)這里n代表ー個警告等級(1一一4)〇pragmawarning(push)保存所有警告信息的現(xiàn)有的警告狀態(tài)。Spragmawarning(push,n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告等級設定為n。Spragmawarning(pop)向棧中彈出最后ー個警告信息,在入棧和出棧之間所作的一切改動取消。例如:pragmawarning(push)pragmawarning(disable:4705)ttpragmawarning(disable:4706)Spragmawarning(disable:4707)// Spragmawarning(pop)在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)。(7)#pragmacomment(...)該指令將一個注釋記錄放入ー個對象文件或可執(zhí)行文件中。常用的lib關鍵字,可以幫我們連入ー個庫文件。如:ttpragmacomment(lib,"comctl32.lib)pragmacomment(lib,〃vfw32.lib")ttpragmacomment(lib,"wsock32,lib)每個編譯程序可以用#pragma指令激活或終止該編譯程序支持的ー些編譯功能。例如,對循環(huán)優(yōu)化功能:Spragmaloop_opt(on) //激活ttpragmaloop_opt(off) /Z終止有時,程序中會有些函數(shù)會使編譯器發(fā)出你熟知而想忽略的警告,如aParameterxxxisneverusedinfunctionxxx”,可以這樣:ttpragmawarn-100 //Turnoffthewarningmessageforwarning#100intinsert_record(REC*r){/*functionbody*/)pragmawarn+100 //Turnthewarningmessageforwarning#100backon函數(shù)會產(chǎn)生一條有唯一特征碼!00的警告信息,如此可暫時終止該警告。每個編譯器對#pragma的實現(xiàn)不同,在ー個編譯器中有效在別的編譯器中兒乎無效。可從編譯器的文檔中查看。補充 #pragmapack與內存對齊問題許多實際的計算機系統(tǒng)對基本類型數(shù)據(jù)在內存中存放的位置有限制,它們會要求這些數(shù)據(jù)的首地址的值是某個數(shù)k(通常它為4或8)的倍數(shù),這就是所謂的內存對齊,而這個k則被稱為該數(shù)據(jù)類型的對齊模數(shù)(alignmentmodulus)〇Win32平臺下的微軟C編譯器(cl.exefor80x86)在默認情況下采用如下的對齊規(guī)則:任何基本數(shù)據(jù)類型T的對齊模數(shù)就是T的大小,即sizeof(T)o比如對于double類型(8字節(jié)),就要求該類型數(shù)據(jù)的地址總是8的倍數(shù),而char類型數(shù)據(jù)(!字節(jié))則可以從任何ー個地址開始。Linux下的GCC奉行的是另外一套規(guī)則(在資料中查得,并未驗證,如錯誤請指正):任何2字節(jié)大小(包括單字節(jié)嗎?)的數(shù)據(jù)類型仕匕如short)的對齊模數(shù)是2,而其它所有超過2字節(jié)的數(shù)據(jù)類型(比如long,double)都以4為對齊模數(shù)。ANSIC規(guī)定一種結構類型的大小是它所有字段的大小以及字段之間或字段尾部的填充區(qū)大小之和。填充區(qū)就是為了使結構體字段滿足內存對齊要求而額外分配給結構體的空間。那么結構體本身有什么對齊要求嗎?有的,ANSIC標準規(guī)定結構體類型的對齊要求不能比它所有字段中要求最嚴格的那個寬松,可以更嚴格。如何使用C/C++中的對齊選項vc6中的編譯選項有/Zp[l]2|4|8|16],/Zpl表示以1字節(jié)邊界對齊,相應的,/Zpn表示以n字節(jié)邊界對齊。n字節(jié)邊界對齊的意思是說,ー個成員的地址必須安排在成員的尺寸的整數(shù)倍地址上或者是n的整數(shù)倍地址上,取它們中的最小值。也就是:min(sizeof(member),n)實際上,I字節(jié)邊界對齊也就表示了結構成員之間沒有空洞。/Zpn選項是應用于整個工程的,影響所有的參與編譯的結構。要使用這個選項,可以在vc6中打開工程屬性頁,c/c++頁,選擇CodeGeneration分類,在Structmemberalignment可以選擇。要專門針對某些結構定義使用對齊選項,可以使用#pragmapack編譯指令:Spragmapack([n])該指令指定結構和聯(lián)合成員的緊湊對齊。而ー個完整的轉換單元的結構和聯(lián)合的緊湊對齊由/Zp選項設置。緊湊對齊用pack編譯指示在數(shù)據(jù)說明層設置。該編譯指示在其出現(xiàn)后的第ー個結構或聯(lián)合說明處生效。該編譯指示對定義無效。當你使用#pragmapack(n)時,這里n為1、2、4、8或16〇第一個結構成員之后的每個結構成員都被在値在更小的成員類型或n字節(jié)界限內。如果你使用無參量的#pragmapack,結構成員被緊湊為以/Zp指定的值。該缺省/Zp緊湊值為/Zp8。(2)編譯器也支持以下增強型語法:#pragmapack([[{push|pop},][identifier,]][n])若不同的組件使用pack編譯指示指定不同的緊湊對齊,這個語法允許你把程序組件組合為ー個單獨的轉換單元。帶push參量的pack編譯指示的每次出現(xiàn)將當前的緊湊對齊立磕到ー個內部編譯器堆棧中。編譯指示的參量表從左到右讀取。如果你使用push,則當前緊湊值被存儲起來;如果你給出ー個n的值,該值將成為新的緊湊值。若你指定一個標識符,即你選定一個名稱,則該標識符將和這個新的的緊湊值聯(lián)系起來。帶ー個pop參量的pack編譯指示的每次出現(xiàn)都會檢索內部編譯器堆棧頂?shù)闹?并且使該值為新的緊湊對齊值。如果你使用pop參量且內部編譯器堆棧是空的,則緊湊值為命令行給定的值,并且將產(chǎn)生一個警告信息。若你使用pop且指定一個n的值,該值將成為新的緊湊值。若你使用p〇p且指定一個標識符,所有存儲在堆棧中的值將從棧中刪除,直到找到ー個匹配的標識符,這個與標識符相關的緊湊值也從棧中移出,并且這個僅在標識符入棧之前存在的緊湊值成為新的緊湊值。如果未找到匹配的標識符,將使用命令行設置的緊湊值,并且將產(chǎn)生一個一級警告。缺省緊湊對齊為8。pack編譯指示的新的增強功能讓你編寫頭文件,確保在遇到該頭文件的前后的緊湊值是一樣的。(3)棧內存對齊在vc6中棧的對齊方式不受結構成員對齊選項的影響。它總是保持對齊,而且對齊在4字節(jié)邊界上。9預定義的宏名ANSI標準說明了五個預定義的宏名。它們是:_line__FILE__DATE__TIME__STDC_如果編譯不是標準的,則可能僅支持以上宏名中的兒個,或根本不支持。記住編譯程序也許還提供其它預定義的宏名。line_及ーFILE一宏指令在有關#line的部分中已討論,這里討論其余的宏名。DATE一宏指令含有形式為月/日/年的串,表示源文件被翻譯到代碼時的日期。源代碼翻譯到目標代碼的時間作為串包含在一TIME_中。串形式為時:分:秒。如果實現(xiàn)是標準的,則宏一sTDC―含有十進制常量lo如果它含有任何其它數(shù),則實現(xiàn)是非標準的。注意:宏名的書寫由標識符與兩邊各二條下劃線構成。10注釋在C語言中,所有的注釋由字符/?開始,以?/結束。在星號及斜杠之間不允許有空格。編譯程序忽略注釋開始符到注釋結束符間的任何文本。例如,下面程序在屏幕上只打印"hell〇”〇main()(printf("hello");/*printf("Thisisasampletoprinthell;0"*/)}注釋可出現(xiàn)在程序的任何位置,但它不能出現(xiàn)在關鍵字或標識符中間。即,注釋x=10+/*addthenumbers*/5:是有效的,但swi/*thiswillnotwork*/tch(c){...是不正確的,因為C的關鍵字不能含有注釋。通常也不希望表達式中間出現(xiàn)注釋,因為這會使意義含混不清。注釋不可嵌套,即ー個注釋內不可含有另ー個注釋。例如,下面代碼段在編譯時出錯:/?thisisanoutercommentx=y/a;/*thisisaninnercomment-andcausesanerror*//當需要解釋程序的行為時,注釋應簡明扼要。除了最簡單和最直觀的函數(shù)外,都應有注釋,在函數(shù)開始處說明其功能,如何調用以及返回何處pragma預處理指令作用說明在所有的預處理指令中,#pragma指令可能算是比較復雜的一條,它的作用是設定編譯器的狀態(tài)或者是指示編譯器完成?些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C++語言完全兼容的情況下,給出主機或操作系統(tǒng)專有的特征。依據(jù)定義,編譯指示是機器或操作系統(tǒng)專有的,且對于每個編譯器都是不同的。其格式一般為:#pragmapara:其中para為參數(shù),下面我們就一些常用的參數(shù)作一?些說明。message參數(shù):message參數(shù)主要用于在編譯信息輸出窗口中輸出相應的信息;當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來;這對于源代碼信息的控制是非常重要的;其格式如下:#pragmamessage("消息文本”)當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什么地方定義了一X86這個宏可以用下面的方法:#ifdef_X86#pragmamessage("_X86macroactivated!w)#endif當我們定義了一X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示"_X86macroactivated!v。我們就不會因為不記得自己定義的ー些特定的宏市抓耳撓腮了。code_seg參數(shù):此參數(shù)主要用于設置程序中函數(shù)代碼存放的代碼段,當我們開發(fā)驅動程序的時候就會使用到它。其格式如下:#pragmacode_seg([[{pushpop),][identifier,」」Lsegment-name”[,"segment-class"]])該指令用來指定函數(shù)在?obj文件中存放的節(jié),觀察OBJ文件可以使用VC自帶的dumpbin命令行程序,函數(shù)在?obj文件中默認的存放節(jié)為.text節(jié),如果code_seg沒有帶參數(shù)的話,則函數(shù)存放在.text節(jié)中;push(可選參數(shù))將一個記錄版到內部編譯器的堆棧中;可選參數(shù)可以為…個標識符或者節(jié)名;pop(可選參數(shù))將一個記錄從堆棧頂端彈出,該記錄可以為ー個標識符或者節(jié)名;identifier(可選參數(shù))當使用push指令時,為壓入堆棧的記錄指派的ー個標識符,當該標識符被刪除的時候和其相關的堆棧中的記錄將被彈出堆棧;"segment-name”(可選參數(shù))表示函數(shù)存放的節(jié)名。例如:〃默認情況下,函數(shù)被存放在.text節(jié)中voidfund(){//storedin.text)〃將函數(shù)存放在.my_datal節(jié)中#pragmacode_seg(".my_datal")voidfunc2(){//storedinmydatal)//rl為標識符,將函數(shù)放入.my_data2節(jié)中#pragmacode_seg(push,rl,".my_data2")voidfunc3(){ //storedinmy_data2}intmain(){)once參數(shù):這是ー個比較常用的能數(shù),只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次;其用法如下:#pragmaoncehdrstop參數(shù):hdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯;BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除ー些頭文件;有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯;你可以用#pragmastartup指定編譯優(yōu)先級,如果使用了#pragmapackage(smart一init),BCB就會根據(jù)優(yōu)先級的大小先后編譯;其用法如下:#pragmahdrstop

5.warning5.warning參數(shù):該指令允許有選擇性的修改編譯器的警告消息的行為;指令格式如下:ttpragmawarning(warning-spec:warning-number-list[;warning-spec:warning-number-list...])#pragmawarning(push[,n])#pragmawarning(pop)主要用到的警告表示有如下幾個:once:只顯示一次(警告/錯誤等)消息default:重置編譯器的警告行為到默認狀態(tài)1,2,3,4:四個警告級別disable:禁止指定的警告信息error:將指定的警告信息作為錯誤報告如果大家對上面的解釋不是很理解,可以參考一下下面的例子及說明:#pragmawarning(disable:450734;once:4385;error:164)這條指令等價于如下指令:ttpragmawarning(disable:450734)//不顯示4507和34號警告信息#pragmawarning(once:4385) //4385號警告信息僅報告一次ttpragmawarning(error:164)/Z把164號警告信息作為ー個錯誤。同時這個pragmawarning也支持如下格式:#pragmawarning(push[,n])ttpragmawarning(pop)這里n代表一個警告等級(1一一4)〇ttpragmawarning(push)保存所有警告信息的現(xiàn)有的警告狀態(tài)。#pragmawarning(push,n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告等級設定為n。#pragmawarning(pop)向棧中彈出最后一,個警告信息,在入棧和出棧之間所作的一切改動取消。例如:#pragmawarning(push)pragmawarning(disable:4705)pragmawarning(disable:4706)pragmawarning(disable:4707)pragmawarning(pop)在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707);在使用標準C++進行編程的時候經(jīng)常會得到很多的警告信息,而這些警告信息都是不必要的提示,所以我們可以使用#pragmawarning(disable:4786)來禁止該類型的警告;在vc中使用AD0的時候也會得到不必要的警告信息,這個時候我們可以通過#pragmawarning(disable:4146)來消除該類型的警告信息。comment參數(shù):該參數(shù)將一個注釋記錄放入ー個對象文件或可執(zhí)行文件中;格式如下:#pragmacomment(comment-type[,commentstring])comment-type(注釋類型):可以指定為五種預定義的標識符的其中一種五種預定義的標識符如下:0 compiler:將編譯器的版本號和名稱放入目標文件中,本條注釋記錄將被編譯器忽略如果你為該記錄類型提供了commentstring參數(shù),編譯器將會產(chǎn)生一個警告。例如:ftpragmacomment(compiler)0 exestr將commentstring參數(shù)放入目標文件中,在鏈接的時候這個字符串將被放入到可執(zhí)行文件中,當操作系統(tǒng)加載可執(zhí)行文件的時候,該參數(shù)字符串不會被加載到內存中.但是,該字符串可以被dumpbin之類的程序查找出并打印出來,你可以用這個標識符將版本號碼之類的信息嵌入到可執(zhí)行文件中!0 lib這是一個非常常用的關鍵字,用來將一個庫文件鏈接到目標文件中常用的lib關鍵字,可以幫我們連入ー個庫文件。例如:#pragmacomment(lib,“user32.lib")該指令用來將user32.1ib庫文件加入到本工程中。0 linker將一個鏈接選項放入目標文件中,你可以使用這個指令來代替由命令行傳入的或者在開發(fā)環(huán)境中設置的鏈接選項,你可以指定/include選項來強制包含某個對象,例如:ttpragmacomment(linker,"/include: mySymbol")你可以在程序中設置下列鏈接選項:/DEFAULTLIB/EXPORT/INCLUDE/MERGE/SECTION這些選項在這里就不一,ー說明了,詳細信息請看msdn!0 user將一般的注釋信息放入目標文件中commentstring參數(shù)包含注釋的文本信息,這個注釋記錄將被鏈接器忽略。例如:#pragmacomment(user,"Compiledon"_DATE"at"_TIME)pack參數(shù):此參數(shù)主要用于設置結構體中變量的對齊方式:如果設置的值比結構體中字節(jié)最長的類型還要大,則這個變量(注意僅針對這ー個變量)只按照它的字節(jié)長度對齊,即不會出現(xiàn)內存浪費的情況;其對齊有1、2、4、8、16,其中默認值為8;其用格式如下:#pargmapack(n)例子例如:〃每個變量按照1字節(jié)對齊Wpragmapack(1)structA〃每個變量按照1字節(jié)對齊inty; //alignedonbyteboundary1);sizeof(A)==5^pragmapack(2) 〃每個變量按照2字節(jié)對齊structA(charx; //alignedonbyteboundary0inty; //alignedonbyteboundary2};sizeof(A)==6#pragmapack(4) 〃每個變量按照4字節(jié)對齊structA(charx; //alignedonbyteboundary0inty; //alignedonbyteboundary4};sizeof(A)ニニ8Spragmapack() 〃默認,相當于#pragmapack(8)每個變量按照8字節(jié)對齊structAcharx; //alignedonbyteboundary0inty; //alignedonbyteboundary4};sizeof(A)==8但是這里y的大小是4字節(jié),所以不會按照8字節(jié)對齊,否則將造成1個int空間的浪費:在本人的工作當中這條指令主要用于網(wǎng)絡數(shù)據(jù)包傳輸中用的比較多,畢竟現(xiàn)在的計算機上內存都比較大,所以浪費一點空間也是可以忍受的,更何況浪費的同時能給我們的處理速度帶來一定的提高剛接觸到MFC編程的人往往會被MFC向導生成的各種宏定義和預處理指令所嚇倒,但是預處理和宏定義又是C語言的ー個強大工具。使用它們可以進行簡單的源代碼控制,版本控制,預警或者完成?些特殊的功能。ー個經(jīng)典的例子使用預處理與宏定義最經(jīng)典的例子莫過于加在一個頭文件中以避免頭文件被兩次編譯。試想這種的情況,有一ー個文件headerfile,h它被包含在headerfilel.h中,同時在headerfile2.h中也被包含了,現(xiàn)在有一個CPP文件,implement,cpp包含了headerfilel.h和headerfile2.h:#includeKheaderfilel.hw#include"headerfile2.h”假設headerfile.h中定義了一個全局變量iglobal〇intiglobal;在編譯的時候編譯器兩次編譯headerfile,也就會發(fā)現(xiàn)iglobal被定義了兩次,這時就會發(fā)生變量重定義的編譯錯誤。傳統(tǒng)的解決辦法是使用#ifdef以及#endif來避免頭文件的重復編譯,在上面的例子中,只需要加上這么幾行:#ifndefsmartnose_2002_6_2Iheaderfi1eh#definesmartnose_2002_6_2l_headerfile_hintiglobal;#endif仔細的考慮上面的宏定義,會發(fā)現(xiàn)當編譯器編譯過一次headerfile.h以后,smartnose_2002_6_21_headerfile_h這個宏就被定義了,以后對headerfile,h的編譯都會跳過intiglobal這一行。當然smartnose_2002_6_21_headerfile_h這個宏是可以任意定義的,但是這個宏本身不能和其它文件中見義的宏重復,由以MFC在自動生成的文件中總是使用一個隨機產(chǎn)生的長度非常長的宏,但我覺得這沒有必要,我建議在這個宏中加入ー些有意義的信息,比方作者,文件名,文件創(chuàng)建時間等等,因為我們有時候會忘記在注釋中加入這些信息。在VC.Net中我們不會再看見這些宏定義了,因為在這里會普遍使用ー個預處理指令:#pragmaonce只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經(jīng)有了,但是考慮到兼容性并沒有太多的使用它。源代碼版本控制當我們?yōu)樵S多平臺開發(fā)多個版本的時候預編譯指令和宏定義也能夠幫我們的忙。假設我們現(xiàn)在為WINDOWS和LINUX開發(fā)了一套軟件,由于這兩種系統(tǒng)的不同,我們不得不在程序控制源代碼的版本。比方內存的分配,我們可以在LINUX上使用標準C的malloc函數(shù),但是我們希望在WINDOWS上使用HeapAllocAPI〇下面的代碼演示了這種情況:main()#ifdef_WINDOWS_PLATFORMHeapAlloc(5);#elsemalloc(5);Sendif當我們在WINDOWS平臺上編譯此程序的時候,只需要定義WINDOWS-PLATFORM這個宏,那么HeapAlloc這條語句就能夠起作用了。這樣就能夠讓我們在同一個文件中為不同的平臺實現(xiàn)不同版本的代碼,同時保持程序的良好結構。在許多情況下,我們還可以為ー個方法使用不同的算法,然后用宏定義來針對不同的情況選擇其中的ー個進行編譯。這在MFC應用程序中是使用得最多的。最明顯的就是文件中經(jīng)常存在的#ifdef_DEBUG some code Sendif這樣的代碼,這些代碼在應用程序的調試版(DEBUG)中會發(fā)揮其作用。SPragma指令在所有的預處理指令中,SPragma指令可能是最復雜的了,它的作用是設定編譯器的狀態(tài)或者是指示編譯器完成一些特定的動作。其格式一般為SPragmaPara其中Para為參數(shù),下面來看一些常用的參數(shù)。message參數(shù)。Message參數(shù)是我最喜歡的一個參數(shù),它能夠在編譯信息輸出窗口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:一?message參數(shù)。message它能夠在編譯信息輸出窗口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:Spragmamessage("消息文本”)當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢査。假設我們希望判斷自己有沒有在源代碼的什么地方定義了一X86這個宏可以用下面的方法#ifdef_X86^pragmamessage("_X86macroactivated!")Sendif當我們定義了一X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示X86macroactivated!"〇我們就不會因為不記得自己定義的ー些特定的宏而抓耳撓腮了二.另一個使用得比較多的#pragma參數(shù)是code_seg。格式如:Spragmacode_seg([[{push|pop},][identifier,]][segment-name[,"segment-class*])該指令用來指定函數(shù)在.obj文件中存放的節(jié),觀察OBJ文件可以使用VC自帶的dumpbin命令行程序,函數(shù)在.obj文件中默認的存放節(jié)為.text節(jié)如果code_seg沒有帶參數(shù)的話,則函數(shù)存放在.text節(jié)中push(可施參數(shù))將一個記錄放到內部編譯器的堆棧中,可選參數(shù)可以為ー個標識符或者節(jié)名pop(可選參數(shù))將一個記錄從堆棧頂端彈出,該記錄可以為ー個標識符或者節(jié)名identifier(可選參數(shù))當使用push指令時,為壓入堆棧的記錄指派的ー個標識符,當該標識符被刪除的時候和其相關的堆棧中的記錄將被彈出堆棧"segment-name”(可選參數(shù))表示函數(shù)存放的節(jié)名例如:〃默認情況下,函數(shù)被存放在.text節(jié)中voidfund(){//storedin.text〃將函數(shù)存放在.my_datal節(jié)中Spragmacode_seg(".my_datal")voidfunc2(){//storedinmy_datal}//rl為標識符,將函數(shù)放入.my_data2節(jié)中#pragmacode_seg(push,rl,.my_data2")voidfunc3(){//storedinmy_data2)intmain(){)三.Spragmaonce仕匕較常用)這是ー個比較常用的指令,只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次四.#pragmahdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除一些頭文件。有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragmastartup指定編譯優(yōu)先級,如果使用了#pragmapackage(smart_init),BCB就會根據(jù)優(yōu)先級的大小先后編譯。五.#pragmawarning指令該指令允許有選擇性的修改編譯器的警告消息的行為指令格式如下:^pragmawarning(warning-specifier:warning-number-list[;warning-specifier:warning-number-list...]#pragmawarning(push[,n])#pragmawarning(pop)主要用到的警告表示有如下幾個:once:只顯示一次(警告/錯誤等)消息default:重置編譯器的警告行為到默認狀態(tài)1,2,3,4:四個警告級別disable:禁止指定的警告信息error:將指定的警告信息作為錯誤報告如果大家對上面的解釋不是很理解,可以參考ー?下下面的例子及說明Spragmawarning(disable:450734;once:4385;error:164)等價于:#pragmawarning(disable:450734)//不顯示4507和34號警告信息Spragmawarning(once:4385)//4385號警告信息僅報告一次Spragmawarning(error:164)/Z把164號警告信息作為ー個錯誤。同時這個pragmawarning也支持如下格式:#pragmawarning(push[,n])#pragmawarning(pop)這里n代表ー個警告等級(1—4)〇Spragmawarning(push)保存所有警告信息的現(xiàn)有的警告狀態(tài)。#pragmawarning(push,n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告等級設定為n。ttpragmawarning(pop)向棧中彈出最后ー個警告信息,在入棧和出棧之間所作的一切改動取消。例如:Spragmawarning(push)#pragmawarning(disable:4705)#pragmawarning(disable:4706)Spragmawarning(disable:4707)Spragmawarning(pop)在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)在使用標準C++進行編程的時候經(jīng)常會得到很多的警告信息,面這些警告信息都是不必要的提示,所以我們可以使用#pragmawarning(disable:4786)來禁止該類型的警告在vc中使用ADO的時候也會得到不必要的警告信息,這個時候我們可以通過Spragmawarning(disable:4146)來消除該類型的警告信息六?pragmacomment(...)該指令的格式為#pragmacomment("comment-type*[,commentstring])該指令將一個注釋記錄放入ー個對象文件或可執(zhí)行文件中,comment-type(注釋類型):可以指定為五種預定義的標識符的其中一種五種預定義的標識符為:compiler:將編譯器的版本號和名稱放入目標文件中,本條注釋記錄將被編譯器忽略如果你為該記錄類型提供了commentstring參數(shù),編譯器將會產(chǎn)生?■一個警告例如:#pragmacomment(compiler)exestr:將commentstring參數(shù)放入目標文件中,在鏈接的時候這個字符串將被放入到可執(zhí)行文件中,當操作系統(tǒng)加載可執(zhí)行文件的時候,該參數(shù)字符串不會被加載到內存中.但是,該字符串可以被dumpbin之類的程序査找出并打印出來,你可以用這個標識符將版本號碼之類的信息嵌入到可執(zhí)行文件中!1ib:這是ー個非常常用的關鍵字,用來將一個庫文件鏈接到目標文件中常用的lib關鍵字,可以幫我們連入ー個庫文件。例如:#pragmacomment(lib,“user32.lib")該指令用來將user32.lib庫文件加入到本工程中l(wèi)inker:將一個鏈接選項放入目標文件中,你可以使用這個指令來代替由命令行傳入的或者在開發(fā)環(huán)境中設置的鏈接選項,你可以指定/include選項來強制包含某個對象,例如:#pragmacomment(linker,"/include:_mySymbol")你可以在程序中設置下列鏈接選項/DEFAULTLIB/EXPORT/INCLUDE/MERGE/SECTION這些選項在這里就不ー,ー說明了,詳細信息請看msdn!user:將一般的注釋信息放入目標文件中commentstring參數(shù)包含注釋的文本信息,這個注釋記錄將被鏈接器忽略例如:Spragmacomment(user,"Compiledon"DATE *at* TIME)補充一個Spragmapack(n)控制對齊如Spragmapack(push)ttpragmapack(1)structs_l{charszname[l];inta;);#pragmapack(pop)structs_2{charszname[l];inta;);則printf(,zs_lsize:%d\n〃,sizeof(structs_l));printf(/zs_2size:%d\n〃,sizeof(structs_2));得到5,8〇#pragma的用法#pragma是ー個C語言中的預處理指令,它的作用是設定編譯器的狀態(tài)或者是指示編譯器完成一些特定的動作。依據(jù)定義,編譯指示是機器或操作系統(tǒng)專有的,且對于每個編譯器都是不同的。其格式一般為:ttpragmaPara其中Para為參數(shù),下面來看ー些常用的參數(shù)。(l)message參數(shù)。Message參數(shù)是我最喜歡的ー個參數(shù),它能夠在編譯信息輸出]窗口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:SPragmamessage("消息文本”)當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢査。假設我們希望判斷自己有沒有在源代碼的什么地方定義了一X86這個宏可以用下面的方法#ifdef_X86SPragmamessage("_X86macroactivated!")ftendif當我們定義了一X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示X86macroactivated!"〇我們就不會因為不記得自己定義的ー些特定的宏而抓耳撓腮了(2)另?個使用得比較多的pragma參數(shù)是code_seg0格式如:Spragmacode_seg([“section-name“しsection-class"]])它能夠設置程ホ中函數(shù)代碼存放的代碼段,當我們開發(fā)驅動程序的時候就會使用到它。(3)#pragmaonce(比較常用)只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經(jīng)有了,但是考慮到兼容性并沒有太多的使用它。(4)#pragmahdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除ー些頭文件。有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragmastartup指定編譯優(yōu)先級,如果使用了#pragmapackage(smart_init),BCB就會根據(jù)優(yōu)先級的大小先后編譯。(5)ftpragmaresource dfm”表示把?.dfm文件中的資源加入工程。*.dfm中包括窗體外觀的定義。(6)#pragmawarning(disable:450734;once:4385;error:164)等價于:Spragmawarning(disable:450734)//不顯示4507和34號警告信息Spragmawarning(once:4385)//4385號警告信息僅報告ー一次Spragmawarning(error:164)/Z把164號警告信息作為ー個錯誤。同時這個pragmawarning也支持如下格式:Spragmawarning(push[,n])Spragmawarning(pop)這里n代表ー個警告等級(1一一4)。#pragmawarning(push)保存所有警告信息的現(xiàn)有的警告狀態(tài)。#pragmawarning(push,n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告等級設定為n。#pragmawarning(pop)向棧中彈出最后ー個警告信息,在入棧和出棧之間所作的一切改動取消。例如:#pragmawarning(push)#pragmawarning(disable:4705)Spragmawarning(disable:4706)Spragmawarning(disable:4707)// #pragmawarning(pop)在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)。pragmacomment(...)該指令將一個注釋記錄放入ー個對象文件或可執(zhí)行文件中。常用的lib關鍵字,可以幫我們連入ー個庫文件。progmapack(n)指定結構體對齊方式!Spragmapack(n)來設定變量以n字節(jié)對齊方式。n字節(jié)對齊就是說變量存放的起始地址的偏移量有兩種情況:第一、如果n大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認的對齊方式,第二、如果n小于該變量的類型所占用的字節(jié)數(shù),那么偏移量為n的倍數(shù),不用滿足默認的對齊方式。結構的總大小也有個約束條件,分下面兩種情況:如果n大于所有成員變量類型所占用的字節(jié)數(shù),那么結構的總大小必須為占用空間最大的變量占用的空間數(shù)的倍數(shù);否則必須為n的倍數(shù)。下面舉例說明其用法。Spragmapack(push)〃保存對齊狀態(tài)Spragmapack(4)〃設定為4字節(jié)對齊structtest(charml;doublem4;intm3;};Spragmapack(pop)〃恢復對齊狀態(tài)為測試該功能,可以使用sizeof()測試結構體的長度!VC預定義的宏在VC中有一類宏并不是由用戶用#define語句定義的,而是編譯器本身就能夠識別它們。這些宏的作用也是相當大的。讓我們來看第一個,也是MFC中使用得最頻繁的ー個:_FILE_〇當編譯器遇到這個宏時就把它展開成當前被編譯文件的文件名。好了,我們馬上就可以想到可以用它來做什么,當應用程序發(fā)生錯誤時,我們可以報告這個錯誤發(fā)生的程序代碼在哪個文件里,比方在文件test,cpp中有這樣的代碼:try{'char*p=new(char[10]);)catch(CException*e)(TRACE(athereisanerrorinfile:%s\n”,_FILE );在程序運行的時候,如果內存分配出現(xiàn)了錯誤,那么在調試窗口中會出現(xiàn)thereisanerrorinfile:test,cpp這句話,當然,我們還可以把這個錯誤信息顯示在別的地方。如果我們還能夠記錄錯誤發(fā)生在哪一行就好了,幸運的是,與_FILE_宏定義ー樣,還有一個宏記錄了當前代碼所在的行數(shù),這個宏是_LINE_。使用上面的兩個宏,我們可以寫出ー個類似于VC提供的ASSERT語句。下面是方法#defineMyAssert(x)\if(!(x))\MessageBox(_FILE_,_LINE_,NULL,MB_OK);我們在應用程序中可以象使用ASSERT語句ー樣使用它,在錯誤發(fā)生時,它會彈出ー個對話框,其標題和內容告訴了我們錯誤發(fā)生的文件和代碼行號,方便我們的調試,這對于不能使用ASSERT語句的項目來說是非常有用的。除了這兩個宏以外,還有記錄編譯時間的_TIME_,記錄日期的_DATE_,以及記錄文件修改時間的ーTIMESTAMP_宏。使用這些預定義的宏,我們兒乎可以生成和VC能夠生成的一樣完整的源代碼信息報表。結論翻開MFC和Linux的源代碼,宏定義幾乎占據(jù)了半邊天,消息映射,隊列操作,平臺移植,版本管理,甚至內核模塊的拆卸安裝都用宏定義完成。毫不夸張的說,有些文件甚至就只能看見宏定義。所以學習宏定義,熟練的使用宏定義對于學習C語言乃至VC都是非常關鍵的。在上一篇文章中,我演示了兒個常用的宏定義和預處理指令,但可以說這些都是相當常規(guī)的技巧。下面要介紹的宏定義與預處理指令的用法也是ATL,MFC以及LINUX中使用得比較多的非常重要的技巧。##連接符與#符##連接符號由兩個井號組成,其功能是在帶參數(shù)的宏定義中將兩個子串(token)聯(lián)接起來,從而形成一個新的子串。但它不可以是第一個或者最后一個子串。所謂的子串(token)就是指編譯器能夠識別的最小語法單元。具體的定義在編譯原理里有詳盡的解釋,但不知道也無所謂。同時值得注意的是#符是把傳遞過來的參數(shù)當成字符串進行替代。下面來看看它們是怎樣工作的。這是MSDN上的ー個例子。假設程序中已經(jīng)定義了這樣ー個帶參數(shù)的宏:Sdefinepaster(n)printf("token"#n"=%d",token##n)同時又定義了一個整形變量:inttoken9=9;現(xiàn)在在主程序中以下面的方式調用這個宏:paster(9);那么在編譯時,上面的這句話被擴展為:printf("token""9""=%d",token9);注意到在這個例子中,paster(9);中的這個"9"被原封不動的當成了一個字符串,與“token”連接在了一起,從而成為了token9o而#n也被“9”所替代??上攵?上面程序運行的結果就是在屏幕上打印出token9=9在ATL的編程中,我們查看它的源代碼就會經(jīng)??匆娺@樣的一段:#defineIMPLEMENTS_INTERFACE(Itf)\{&IID_##Itf,ENTRY_IS_OFFSET,BASE_OFFSET(_ITCls,Itf)},我們經(jīng)常不假思索的這樣使用它:IMPLEMENTS_INTERFACE(ICat)實際上IID」Cat已經(jīng)在別的地方由ATL向導定義了。當沒有向導的時候,你只要遵循把IIDJJロ在你的接口名前面來定義GUID的規(guī)則就也可以使用這個宏。在實際的開發(fā)過程中可能很少用到這種技巧,但是ATL使用得如此廣泛,而其中又出現(xiàn)了不少這樣的源代碼,所以明白它是怎么一回事也是相當重要的。我的?個朋友就是因為不知道IMPLEMENTSJNTERFACE宏是怎么定義的,而又不小心改動了!ID_ICat的定義而忙活了一整天。Linux的怪圈在剛開始閱讀Linux的時候有一個小小的宏讓我百思不得其解:#definewaitevent(wq,condition)\do{\if(condition)\break;\_wait_event(wq,condition);\}while(0)這是一個奇怪的循環(huán),它根本就只會運行一次,為什么不去掉外面的do{..}while結構呢?我曾一度在心里把它叫做“怪圈”。原來這也是非常巧妙的技巧。在工程中可能經(jīng)常會引起麻煩,而上面的定義能夠保證這些麻煩不會出現(xiàn)。下面是解釋:假設有這樣一個宏定義Sdefinemacro(condition)\if(condition)dosomethingO;現(xiàn)在在程序中這樣使用這個宏:if(temp)macro(i);elsedoanotherthing();一切看起來很正常,但是仔細想想。這個宏會展開成:if(temp)if(condition)dosomethingO;elsedoanotherthing();這時的else不是與第一個if語句匹配,而是錯誤的與第二個if語句進行了匹配,編譯通過了,但是運行的結果一定是錯誤的。為了避免這個錯誤,我們使用do{….}while(0)把它包裹起來,成為…個獨立的語法單元,從而不會與上下文發(fā)生混淆。同時因為絕大多數(shù)的編譯器都能夠識別do{…}while(O)這種無用的循環(huán)并進行優(yōu)化,所以使用這種方法也不會導致程序的性能降低。幾個小小的警告正如微軟聲稱的ー樣,宏定義與預編譯器指令是強大的,但是它又使得程序難以調試。所以在定義宏的時候不要節(jié)省你的字符串,一定要力爭完整的描述這個宏的功能。同時在定義宏的時候如有必要(比方使用了if語句)就要使用do{…}while(O)將它封閉起來。在宏定義的時候一定要注意各個宏之間的相互依賴關系,盡量避免這種依賴關系的存在。下面就有這樣?個例子。設有一個靜態(tài)數(shù)組組成的整型隊列,在定義中使用了這樣的方法:intarray[]={5,6,7,8};我們還需要在程序中遍歷這個數(shù)組。通常的做法是使用?個宏定義#defineELE一NUM4for(intI=O;KELE_NUM;I++)(coutくくarray[I];由于某種偶然的原因,我們刪除了定義中的ー個元素,使它變成:array[]={5,6,7}而卻忘了修改ELE_NUM的值。那么在上面的代碼中馬上就會發(fā)生訪問異常,程序崩潰。然后是徹夜ネ眠的調試,最后發(fā)現(xiàn)問題出在這個宏定義上。解決這個問題的方法是不使用array[]={….}這樣的定義,而顯式的申明數(shù)組的大?。篴rray[ELENUM]={?,,.}這樣在改動數(shù)組定義的時候,我們就不會不記得去改宏定義了??傊?就是在使用宏定義的時候能夠用宏定義的地方統(tǒng)統(tǒng)都用上。我發(fā)現(xiàn)的另ー個有趣的現(xiàn)象是這樣的:假設現(xiàn)在有一個課程管理系統(tǒng),學生的人數(shù)用宏定義為:SdefineSTU_NUM50而老師的人數(shù)恰好也是50人,于是很多人把所有涉及到老師人數(shù)的地方通通用上STLNUM這個宏。另ー個學期過去,學生中的一個被開除了,系統(tǒng)需要改變。怎么辦呢?簡單的使用#defineSTU_NUM49么?如果是這樣,ー個老師也就被開除了,我們不得不手エ在程序中去找那些STU_NUM宏然后判斷它是否是表示學生的數(shù)目,如果是,就把它改成49。天哪,這個宏定義制造的麻煩比使用它帶來的方便還多。正確的方法應該是為老師的數(shù)目另外定義ー個宏:^defineTEANUM50當學生的數(shù)目改變以后只要把STU_NUM定義為49就完成了系統(tǒng)的更改。所以,當程序中的兩個量之間沒有必然聯(lián)系的時候一定不要用其中的ー個宏去替代另ー個,那只會讓你的程序根本無法改動。最后,建議C/C++語言的初學者盡可能多的在你的程序中使用宏定義和預編譯指令。多看看MFC,ATL或者LINUX的源代碼,你會發(fā)現(xiàn)C語言強大的原因所在。在#Pragma是預處理指令它的作用是設定編譯器的狀態(tài)或者是指示編譯器完成ー些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C++語言完全兼容的情況下,給出主機或操作系統(tǒng)專有的特征。依據(jù)定義,編譯指示是機器或操作系統(tǒng)專有的,且對于每個編譯器都是不同的。其格式ー一般為:^PragmaPara其中Para為參數(shù),下面來看一些常用的參數(shù)。(1)message參數(shù)。Message參數(shù)是我最喜歡的?,個參數(shù),它能夠在編譯信息輸出窗口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:#Pragmamessage("消息文本”)當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什么地方定義了一X86這個宏可以用下面的方法#ifdef_X86^Pragmamessage("_X86macroactivated!")#endif當我們定義了一X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示X86macroactivated!"〇我們就不會因為不記得自己定義的ー些特定的宏而抓耳撓腮了。(2)另ー個使用得比較多的pragma參數(shù)是code_sego格式如:#pragmacode_seg([\section-name\[,ヽsection-classヽ[])它能夠設置程序中函數(shù)代碼存放的代碼段,使用沒有section-name字符串的#pragmacode_seg可在編譯開始時將其復位,當我們開發(fā)驅動程序的時候就會使用到它。(3)#pragmaonce(比較常用)只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經(jīng)有了,但是考慮到兼容性并沒有太多的使用它。(4)#pragmahdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除一些頭文件。有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#Pragmastartup指定編譯優(yōu)先級,如果使用了#pragmapackage(smart_init),BCB就會根據(jù)優(yōu)先級的大小先后編譯。(5)#pragmaresource\*.dfmゝ表示把?.dfm文件中的資源加入工程。?.dfm中包括窗體外觀的定義。#pragmawarning(disable:450734;once:4385;error:164)等價于:Spragmawarning(disable:450734)//不顯示4507和34號警告信息#pragmawarning(once:4385)//4385號警告信息僅報告一次Spragmawarning(error:164)/Z把164號警告信息作為ー個錯誤。同時這個pragmawarning也支持如下格式:#pragmawarning(push[,n])這里n代表ー個警告等級(1—4)〇#pragmawarning(push)保存所有警告信息的現(xiàn)有的警告狀態(tài)。#pragmawarning(push,n)保存所有警告信息的現(xiàn)有的警告狀態(tài),并且把全局警告等級設定為n。#pragmawarning(pop)向棧中彈出最后ー個警告信息,在入棧和出棧之間所作的一切改動取消。例如:#pragmawarning(push)Spragmawarning(disable:4705)#pragmawarning(disable:4706)#pragmawarning(disable:4707)// #pragmawarning(pop)在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)。pragmacomment(...)該指令將一個注釋記錄放入ー個對象文件或可執(zhí)行文件中。常用的lib關鍵字,可以幫我們連入ー個庫文件。?通過#pragmapack(n)改變C編譯器的字節(jié)對齊方式在C語言中,結構是ー種復合數(shù)據(jù)類型,其構成元素既可以是基本數(shù)據(jù)類型(如int、long、float等)的變量,也可以是ー些復合數(shù)據(jù)類型(如數(shù)組、結構、聯(lián)合等)的數(shù)據(jù)單元。在結構中,編譯器為結構的每個成員按其自然對界(alignment)條件分配空間。各個成員按照它們被聲明的順序在內存中順序存儲,第一個成員的地址和整個結構的地址相同。例如,下面的結構各成員空間分配情況:structtest(charxl;shortx2;floatx3;charx4;);結構的第一個成員xl,其偏移地址為〇,占據(jù)了第1個字節(jié)。第二個成員x2為short類型,其起始地址必須2字節(jié)對界,因此,編譯器在x2和xl之間填充了ー個空字節(jié)。結構的第三個成員

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論