青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大龍的博客

常用鏈接

統計

最新評論

如此的宏用意何在?------------------------------------#define wait_event(wq,condition) do{ if(condition) break; __wait_event(wq,condition); }while(0)

VC中預處理指令與宏定義的妙用
剛接觸到MFC編程的人往往會被MFC?向導生成的各種宏定義和預處理指令所嚇倒,但是預處理和宏定義又是C語言的一個強大工具。使用它們可以進行簡單的源代碼控制,版本控制,預警或者完成一些特殊的功能。

  一個經典的例子

  使用預處理與宏定義最經典的例子莫過于加在一個頭文件中以避免頭文件被兩次編譯。試想這種的情況,有一個文件headerfile.h?它被包含在headerfile1.h中,同時在headerfile2.h?中也被包含了,現在有一個CPP文件,implement.cpp?包含了headerfile1.h?和headerfile2.h:

#include?“headerfile1.h”
#include?“headerfile2.h”?

  假設headerfile.h?中定義了一個全局變量?iglobal?。

int?iglobal;??

  在編譯的時候編譯器兩次編譯headerfile,也就會發現iglobal被定義了兩次,這時就會發生變量重定義的編譯錯誤。

  傳統的解決辦法是使用#ifdef?以及#endif?來避免頭文件的重復編譯,在上面的例子中,只需要加上這么幾行:

#ifndef?smartnose_2002_6_21_headerfile_h
#define?smartnose_2002_6_21_headerfile_h

int?iglobal;

#endif?

  仔細的考慮上面的宏定義,會發現當編譯器編譯過一次headerfile.h以后,smartnose_2002_6_21_headerfile_h?這個宏就被定義了,以后對headerfile.h的編譯都會跳過int?iglobal?這一行。當然smartnose_2002_6_21_headerfile_h?這個宏是可以任意定義的,但是這個宏本身不能和其它文件中定義的宏重復,所以MFC在自動生成的文件中總是使用一個隨機產生的長度非常長的宏,但我覺得這沒有必要,我建議在這個宏中加入一些有意義的信息,比方作者,文件名,文件創建時間等等,因為我們有時候會忘記在注釋中加入這些信息。

  在VC.Net?中我們不會再看見這些宏定義了,因為在這里會普遍使用一個預處理指令:

#pragma?once?

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

  源代碼版本控制

  當我們為許多平臺開發多個版本的時候預編譯指令和宏定義也能夠幫我們的忙。假設我們現在為WINDOWS?和LINUX開發了一套軟件,由于這兩種系統的不同,我們不得不在程序控制源代碼的版本。比方內存的分配,我們可以在LINUX上使用標準C的malloc?函數,但是我們希望在?WINDOWS上使用HeapAlloc?API。下面的代碼演示了這種情況:

main()
{
………………..
#ifdef?_WINDOWS_PLATFORM
HeapAlloc(5);
#else
malloc(5);
#endif
………………..
}?

  當我們在WINDOWS?平臺上編譯此程序的時候,只需要定義_WINDOWS_PLATFORM這個宏,那么HeapAlloc這條語句就能夠起作用了。這樣就能夠讓我們在同一個文件中為不同的平臺實現不同版本的代碼,同時保持程序的良好結構。在許多情況下,我們還可以為一個方法使用不同的算法,然后用宏定義來針對不同的情況選擇其中的一個進行編譯。這在MFC應用程序中是使用得最多的。最明顯的就是文件中經常存在的

#ifdef?_DEBUG

…………………….some?code………..

#endif?

  這樣的代碼,這些代碼在應用程序的調試版(DEBUG)中會發揮其作用。

  #Pragma?指令

  在所有的預處理指令中,#Pragma?指令可能是最復雜的了,它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。其格式一般為

  #Pragma?Para

  其中Para?為參數,下面來看一些常用的參數。

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

#Pragma?message(“消息文本”)

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

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

#ifdef?_X86

#Pragma?message(“_X86?macro?activated!”)

#endif?

  當我們定義了_X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示“_X86?macro?activated!”。我們就不會因為不記得自己定義的一些特定的宏而抓耳撓腮了。

  另一個使用得比較多的pragma參數是code_seg。格式如:

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

  它能夠設置程序中函數代碼存放的代碼段,當我們開發驅動程序的時候就會使用到它。

  最后一個比較常用的就是上面所說的#pragma?once?指令了。

  VC預定義的宏

  在VC中有一類宏并不是由用戶用#define語句定義的,而是編譯器本身就能夠識別它們。這些宏的作用也是相當大的。讓我們來看第一個,也是MFC中使用得最頻繁的一個:__FILE__?。

  當編譯器遇到這個宏時就把它展開成當前被編譯文件的文件名。好了,我們馬上就可以想到可以用它來做什么,當應用程序發生錯誤時,我們可以報告這個錯誤發生的程序代碼在哪個文件里,比方在文件test.cpp中有這樣的代碼:

try
{
char?*?p=new(char[10]);
}
catch(CException?*e?)
{
TRACE(“?there?is?an?error?in?file:?%s\n”,__FILE__);
}?

  在程序運行的時候,如果內存分配出現了錯誤,那么在調試窗口中會出現there?is?an?error?in?file:?test.cpp?這句話,當然,我們還可以把這個錯誤信息顯示在別的地方。

  如果我們還能夠記錄錯誤發生在哪一行就好了,幸運的是,與__FILE__宏定義一樣,還有一個宏記錄了當前代碼所在的行數,這個宏是__LINE__。使用上面的兩個宏,我們可以寫出一個類似于VC提供的ASSERT語句。下面是方法

#define?MyAssert(x)?if(!(x))?MessageBox(__FILE__,__LINE__,NULL,MB_OK);?

  我們在應用程序中可以象使用ASSERT語句一樣使用它,在錯誤發生時,它會彈出一個對話框,其標題和內容告訴了我們錯誤發生的文件和代碼行號,方便我們的調試,這對于不能使用ASSERT語句的項目來說是非常有用的。

  除了這兩個宏以外,還有記錄編譯時間的__TIME__,記錄日期的__DATE__,以及記錄文件修改時間的__TIMESTAMP__宏。

  使用這些預定義的宏,我們幾乎可以生成和VC能夠生成的一樣完整的源代碼信息報表。

  結論

  翻開MFC和Linux的源代碼,宏定義幾乎占據了半邊天,消息映射,隊列操作,平臺移植,版本管理,甚至內核模塊的拆卸安裝都用宏定義完成。毫不夸張的說,有些文件甚至就只能看見宏定義。所以學習宏定義,熟練的使用宏定義對于學習C語言乃至VC都是非常關鍵的。

在上一篇文章中,我演示了幾個常用的宏定義和預處理指令,但可以說這些都是相當常規的技巧。下面要介紹的宏定義與預處理指令的用法也是ATL,MFC以及LINUX中使用得比較多的非常重要的技巧。?

  ##?連接符與#?符

  ##?連接符號由兩個井號組成,其功能是在帶參數的宏定義中將兩個子串(token)聯接起來,從而形成一個新的子串。但它不可以是第一個或者最后一個子串。所謂的子串(token)就是指編譯器能夠識別的最小語法單元。具體的定義在編譯原理里有詳盡的解釋,但不知道也無所謂。同時值得注意的是#符是把傳遞過來的參數當成字符串進行替代。下面來看看它們是怎樣工作的。這是MSDN上的一個例子。

  假設程序中已經定義了這樣一個帶參數的宏:

#define?paster(?n?)?printf(?"token"?#n?"?=?%d",?token##n?)

  同時又定義了一個整形變量:

int?token9?=?9;

  現在在主程序中以下面的方式調用這個宏:

paster(?9?);

  那么在編譯時,上面的這句話被擴展為:

printf(?"token"?"9"?"?=?%d",?token9?);

  注意到在這個例子中,paster(9);中的這個”9”被原封不動的當成了一個字符串,與”token”連接在了一起,從而成為了token9。而#n也被”9”所替代。

  可想而知,上面程序運行的結果就是在屏幕上打印出token9=9

  在ATL的編程中,我們查看它的源代碼就會經常看見這樣的一段:

#define?IMPLEMENTS_INTERFACE(Itf)?{&IID_##Itf,?ENTRY_IS_OFFSET,BASE_OFFSET(_ITCls,?Itf)?},

  我們經常不假思索的這樣使用它:

……
IMPLEMENTS_INTERFACE(ICat)
……

  實際上IID_ICat?已經在別的地方由ATL向導定義了。當沒有向導的時候,你只要遵循把IID_加在你的接口名前面來定義GUID的規則就也可以使用這個宏。在實際的開發過程中可能很少用到這種技巧,但是ATL使用得如此廣泛,而其中又出現了不少這樣的源代碼,所以明白它是怎么一回事也是相當重要的。我的一個朋友就是因為不知道IMPLEMENTS_INTERFACE宏是怎么定義的,而又不小心改動了IID_ICat的定義而忙活了一整天。

  Linux的怪圈

  在剛開始閱讀Linux的時候有一個小小的宏讓我百思不得其解:

#define?wait_event(wq,condition)?do{?if(condition)?break;?__wait_event(wq,condition);?}while(0)

  這是一個奇怪的循環,它根本就只會運行一次,為什么不去掉外面的do{..}while結構呢?我曾一度在心里把它叫做“怪圈”。原來這也是非常巧妙的技巧。在工程中可能經常會引起麻煩,而上面的定義能夠保證這些麻煩不會出現。下面是解釋:

  假設有這樣一個宏定義

#define?macro(condition)?
if(condition)?dosomething();

  現在在程序中這樣使用這個宏:

if(temp)
macro(i);
else
doanotherthing();

  一切看起來很正常,但是仔細想想。這個宏會展開成:

if(temp)
if(condition)?dosomething();
else
doanotherthing();

  這時的else不是與第一個if語句匹配,而是錯誤的與第二個if語句進行了匹配,編譯通過了,但是運行的結果一定是錯誤的。

  為了避免這個錯誤,我們使用do{….}while(0)?把它包裹起來,成為一個獨立的語法單元,從而不會與上下文發生混淆。同時因為絕大多數的編譯器都能夠識別do{…}while(0)這種無用的循環并進行優化,所以使用這種方法也不會導致程序的性能降低。

  幾個小小的警告

  正如微軟聲稱的一樣,宏定義與預編譯器指令是強大的,但是它又使得程序難以調試。所以在定義宏的時候不要節省你的字符串,一定要力爭完整的描述這個宏的功能。同時在定義宏的時候如有必要(比方使用了if語句)就要使用do{…}while(0)將它封閉起來。在宏定義的時候一定要注意各個宏之間的相互依賴關系,盡量避免這種依賴關系的存在。下面就有這樣一個例子。

  設有一個靜態數組組成的整型隊列,在定義中使用了這樣的方法:?int?array[]={5,?6,?7,?8};

  我們還需要在程序中遍歷這個數組。通常的做法是使用一個宏定義

#define?ELE_NUM?4
…………………………..
……………………………..

for(int?I=0;I<ELE_NUM;I++)
{
cout<<array[I];
}

  由于某種偶然的原因,我們刪除了定義中的一個元素,使它變成:

array[]={5,6,7}

  而卻忘了修改ELE_NUM的值。那么在上面的代碼中馬上就會發生訪問異常,程序崩潰。然后是徹夜不眠的調試,最后發現問題出在這個宏定義上。解決這個問題的方法是不使用

array[]={….}這樣的定義,而顯式的申明數組的大小:

array[ELE_NUM]={….}

  這樣在改動數組定義的時候,我們就不會不記得去改宏定義了。總之,就是在使用宏定義的時候能夠用宏定義的地方統統都用上。

  我發現的另一個有趣的現象是這樣的:

  假設現在有一個課程管理系統,學生的人數用宏定義為:

#define?STU_NUM?50

  而老師的人數恰好也是50人,于是很多人把所有涉及到老師人數的地方通通用上STU_NUM這個宏。另一個學期過去,學生中的一個被開除了,系統需要改變。怎么辦呢?簡單的使用#define?STU_NUM?49?么?如果是這樣,一個老師也就被開除了,我們不得不手工在程序中去找那些STU_NUM宏然后判斷它是否是表示學生的數目,如果是,就把它改成49。天哪,這個宏定義制造的麻煩比使用它帶來的方便還多。正確的方法應該是為老師的數目另外定義一個宏:

#define?TEA_NUM?50

  當學生的數目改變以后只要把STU_NUM?定義為49就完成了系統的更改。所以,當程序中的兩個量之間沒有必然聯系的時候一定不要用其中的一個宏去替代另一個,那只會讓你的程序根本無法改動。

  最后,建議C/C++語言的初學者盡可能多的在你的程序中使用宏定義和預編譯指令。多看看MFC,ATL或者LINUX的源代碼,你會發現C語言強大的原因所在。

posted on 2006-11-28 12:11 大龍 閱讀(1267) 評論(1)  編輯 收藏 引用

評論

# re: 如此的宏用意何在?------------------------------------#define wait_event(wq,condition) do{ if(condition) break; __wait_event(wq,condition); }while(0) 2006-12-03 12:25 brent

多套一個
{
}
就好了...

課程管理系統,應該讓用戶自己設置學生的人數,教師人數。。

有人建議用定義const變量來代替#define STUDENT_COUNT 50,方便調試

for(int I=0;I<ELE_NUM;I++)
{
cout<<array[I];
}
這個ELE_NUM換成sizeof(array)/sizeof(array[0]) 比較合適。
如果用mfc,直接用CArray<int,int>好了。。
用std的話用vector好了。。
  回復  更多評論   


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区成人精品| 国产精品一区二区欧美| 久久免费视频一区| 国产亚洲网站| 蜜臀av国产精品久久久久| 亚洲高清一区二区三区| 亚洲九九精品| 国产精品美女久久久久久久 | 亚洲国产欧美日韩另类综合| 亚洲人成毛片在线播放女女| 欧美精品高清视频| 午夜国产不卡在线观看视频| 欧美国产日韩一区二区在线观看 | 最新亚洲激情| 欧美日韩国产成人在线免费| 亚洲午夜未删减在线观看| 免费黄网站欧美| 欧美一级网站| 国产精品亚洲激情| 亚洲一区免费在线观看| 欧美激情小视频| 欧美一区二区免费视频| 国产欧美精品| 亚洲精品视频在线播放| 国产精品爽爽爽| 一本色道久久88综合日韩精品| 亚洲三级国产| 欧美在线1区| 亚洲精选久久| 亚洲欧美一区二区三区久久| 欧美天堂亚洲电影院在线播放| 久久久国际精品| 欧美日韩性生活视频| 久久亚洲欧美| 国产精品久久久久久久一区探花| 蜜桃久久精品一区二区| 国产一区成人| 亚洲深夜激情| 亚洲高清视频中文字幕| 亚洲免费综合| 亚洲午夜视频| 午夜精品久久久久| 在线观看一区二区精品视频| 欧美日韩精品在线视频| 亚洲免费伊人电影在线观看av| 国产亚洲一区二区三区在线观看| 欧美日韩国产在线播放| 亚洲国产日韩一区| 一区二区亚洲| 亚洲国产小视频| 在线观看日韩| 日韩亚洲一区在线播放| 亚洲一区一卡| 久久精品99国产精品日本| 久久在线免费观看| 亚洲国产高清自拍| 亚洲视频电影图片偷拍一区| 亚洲尤物视频网| 男女精品网站| 国产精品日本欧美一区二区三区| 国产精品一级久久久| 一色屋精品视频在线看| 亚洲欧洲日本在线| 久久国产精品一区二区| 欧美成人首页| 亚洲尤物视频在线| 欧美日韩午夜激情| 国产老肥熟一区二区三区| 国产日韩免费| 久久久福利视频| 蜜桃av一区| 欧美国产综合一区二区| 国产精品视频yy9299一区| 亚洲国产精品高清久久久| 亚洲欧美日韩中文在线制服| 久久综合一区二区三区| 国产精品久久久久久久7电影 | 国产一区视频在线看| 亚洲精品自在在线观看| 国产精品久久久久久av下载红粉 | 亚洲男女自偷自拍图片另类| 欧美一区二区三区在线观看视频| 久久精品水蜜桃av综合天堂| 久久久久久久波多野高潮日日| 久久永久免费| 国产欧美视频在线观看| 国模叶桐国产精品一区| 午夜精品久久久久99热蜜桃导演| 噜噜噜在线观看免费视频日韩 | 久久精品国产亚洲一区二区| 欧美日韩一区二区三区| 亚洲国产精品成人久久综合一区| 欧美影院视频| 午夜精品福利在线观看| 欧美区视频在线观看| 亚洲精品久久7777| 亚洲日本理论电影| 欧美精品v日韩精品v韩国精品v| 精品999网站| 9久草视频在线视频精品| 亚洲人午夜精品免费| 欧美日韩国产精品一卡| 老司机精品久久| 免费一级欧美片在线观看| 欧美一区二区黄| 久久久国产一区二区| 国产精品欧美精品| 亚洲欧美国产va在线影院| 亚洲性夜色噜噜噜7777| 国产偷久久久精品专区| 国产欧美一区二区三区久久| 国产精品丝袜xxxxxxx| 亚洲福利专区| 国产精品卡一卡二卡三| 亚洲国产精品久久久久秋霞不卡| 亚洲国产成人在线视频| 欧美午夜免费影院| 久久亚洲欧美| 亚洲视频免费看| 亚洲一区二区成人在线观看| 亚洲国产欧美一区二区三区同亚洲| 亚洲国产精品va在看黑人| 欧美精品福利视频| 一本大道久久a久久精二百| 亚洲一区二区在| 狠狠综合久久| 亚洲美女精品一区| 国产精品日本精品| 欧美日韩一区不卡| 久久精品夜色噜噜亚洲aⅴ| 欧美日本一道本| 亚洲韩国日本中文字幕| 国产自产2019最新不卡| 亚洲欧美一区二区三区极速播放| 99精品热视频只有精品10| 欧美成在线观看| 日韩亚洲视频在线| 亚洲免费在线视频| 国产日韩欧美在线看| 亚洲欧洲中文日韩久久av乱码| 亚洲美女诱惑| 国产精品福利在线观看| 欧美伊人久久| 欧美韩国在线| 亚洲欧美日韩国产综合精品二区| 国产精品私拍pans大尺度在线| 欧美一区二区视频网站| 欧美a级片网站| 99精品欧美一区二区蜜桃免费| 欧美日韩国产限制| 宅男精品视频| 免费在线一区二区| 国产精品a久久久久| 亚洲先锋成人| 久久中文欧美| 久久激情综合| 在线成人激情黄色| 欧美精品成人在线| 欧美一级欧美一级在线播放| 在线亚洲欧美专区二区| 亚洲精品久久久久| 免费不卡视频| 老色鬼精品视频在线观看播放| 午夜视频一区二区| 欧美亚洲综合网| 久久精品一区二区三区中文字幕 | 久久精品道一区二区三区| 99热免费精品| 亚洲欧美日韩在线播放| 国产精品久久久久天堂| 亚洲美女毛片| 国产精品性做久久久久久| 老鸭窝91久久精品色噜噜导演| 一本到12不卡视频在线dvd| 欧美国产日本在线| 欧美影院在线| 一区二区不卡在线视频 午夜欧美不卡在 | 欧美成人一二三| 久久av在线| 亚洲综合久久久久| 一区二区激情视频| 亚洲日本欧美| 亚洲三级影院| 一本不卡影院| 亚洲嫩草精品久久| 久久av红桃一区二区小说| 欧美一区二区三区视频| 亚洲综合色丁香婷婷六月图片| 亚洲欧美日韩一区在线| 久久激五月天综合精品| 久久尤物电影视频在线观看| 亚洲第一精品在线| 亚洲国产二区| 欧美伊人久久久久久久久影院| 欧美在线www| 欧美激情亚洲自拍| 国产精品日韩欧美一区| 国产精品视频不卡| 亚洲第一主播视频| 一个色综合av|