C語言條件編譯指令完全攻略

本文章借助視頻與個人所學

c語言的從零開始

在 C 語言中虏等,條件編譯指令可以實現(xiàn)源代碼的部分編譯功能食绿,可以根據(jù)表達式的值或者某個特定的宏來確定編譯條件梳凛,以決定編譯哪些代碼锨苏,不編譯哪些疙教。

使用“#ifndef/#define/#endif”防止頭文件被重復引用。

在 C 語言中伞租,一個文件中可以包含多個頭文件贞谓,而頭文件之間又是可以相互引用的,這將引起一個文件中可能間接多次包含某個頭文件葵诈,從而導致了某些頭文件被重復引用多次裸弦。

例如,有 3 個文件 a.h作喘、b.h 和 c.h理疙,其中 b 文件中包含了 a.h,而 c 文件中又分別包含了 a.h 和 b.h 兩個文件徊都。于是問題出來了,由于嵌套包含文件的原因广辰,頭文件 a.h 被兩次包含在源文件 c 中暇矫。在這里,如果頭文件中沒有防止多次編譯的語句择吊,有可能會引起如下兩種后果:

一:某些頭文件重復引用只是增加了編譯器編譯的工作量李根,導致編譯效率降低,不會引起太大的問題几睛。但是房轿,這里還需要說明的是,對比較大的工程而言,編譯效率低下也將是一件非常痛苦的事情囱持。

二:某些頭文件重復引用夯接,有可能會引起意想不到的嚴重錯誤。比如纷妆,在頭文件中定義了全局變量(雖然這種方式不被推薦盔几,但有時候確實需要這么做),將會導致全局變量被重復定義掩幢。

在 C 語言中逊拍,避免同一個頭文件被多次包含、重復引用际邻,最常用也是最簡單的方法就是利用“#ifndef/#define/#endif”結(jié)構(gòu)產(chǎn)生預處理塊來防止頭文件被重復引用芯丧。如下面的示例代碼所示:

#ifndef __HEADERNAME_H__

#define __HEADERNAME_H__

#endif

在上面的預處理塊中,當?shù)谝淮我茫╥nclude)頭文件時世曾,由于“__HEADERNAME_H__”還沒有被宏定義(define)過缨恒,即滿足“#ifndef__HEADERNAME_H__”,從而執(zhí)行“#define__HEADERNAME_H__”以及其他內(nèi)容度硝。如果因為編碼者的不小心或者嵌套包含等原因造成了這個頭文件被多次引用(include)肿轨,那 么“#ifndef__HEADERNAME_H__”判斷條件將在第二次引用(include)頭文件時得不到滿足,因此不執(zhí)行后面的內(nèi)容蕊程,直接跳到“#endif”椒袍。

通過“#ifndef/#define/#endif”結(jié)構(gòu)產(chǎn)生預處理塊,雖然能夠避免同一個頭文件被多次包含和重復引用藻茂,但也存在一個致命的缺點驹暑,那就是一旦一不小心在不同頭文件中定義了相同的宏名,問題就比較麻煩了辨赐。比如优俘,可能會導致明明看到存在頭文件,而編譯器卻硬說找不到聲明等問題掀序。為了避免這種情況帆焕,保證宏名的唯一性,建議按照 Google 公司的建議不恭,頭文件基于其所在項目源代碼樹的全路徑進行命名叶雹。命名格式為:

<PROJECT>_<PATH>_<FILE>_H_

其中,PROJECT 表示項目名稱换吧,PATH 表示頭文件相對路徑折晦,F(xiàn)ILE 表示文件名,再以“_H_”作為后綴沾瓦。比如满着,在項目 CashRegister 中谦炒,現(xiàn)在該項目所在目錄下的一個名為 xml 的子文件夾下的一個 parser 頭文件,則宏定義如下:

#ifndef CASHREGISTER_XML_PARSER_H_

#define CASHREGISTER_XML_PARSER_H_

#endif

當然风喇,基于命名習慣原因宁改,也可以這樣來寫:

#ifndef _CASHREGISTER_XML_PARSER_H_

#define _CASHREGISTER_XML_PARSER_H_

#endif

這里需要注意的是,由于編譯器在每次編譯時都需要打開頭文件才能判定是否有重復定義响驴,因此在編譯大型項目時透且,“#ifndef”會使編譯時間相對較長。

除此之外豁鲤,你還可以使用“#pragma once”方式來防止頭文件被重復引用秽誊,該方式一般由編譯器提供,可以保證同一個文件不會被包含多次琳骡。但這里需要特別說明的是锅论,該方式受編譯器的限制,有些編譯器并不支持該指令楣号,因此在兼容性方面表現(xiàn)得不是很好最易。這里建議為了代碼的兼容性,寧肯降低一些編譯性能炫狱,還是使用“#ifndef/#define/#endif”結(jié)構(gòu)藻懒。

使用條件編譯指令實現(xiàn)源代碼的部分編譯

前面已經(jīng)說過,條件編譯指令可以使編譯器按不同的條件編譯不同的程序部分视译,因而產(chǎn)生不同的目標代碼文件嬉荆。這對于程序的移植和調(diào)試是很有用的,尤其是針對于跨平臺程序移植的時候酷含。在 C 語言中鄙早,主要有如下條件編譯指令。

1椅亚、#if指令

該指令檢測表達式值是否為真限番。如果表達式的值為真,則編譯后面的代碼直到出現(xiàn) #else呀舔、#elif 或 #endif 為止弥虐,否則不編譯。

2媚赖、#endif指令

該指令用于終止 #if 指令霜瘪。

3、#else指令

該指令用于 #if 指令之后省古,當前面的 #if 指令的條件不為真時粥庄,就編譯 #else 后面的代碼丧失。

4豺妓、#elif指令

該指令綜合了 #else 和 #if 指令的作用。下面的示例代碼演示了 #if、#else琳拭、#elif 與 #endif 的組合使用情況训堆。

#if OS==1

? ? printf("V1.0");

#elif OS==2

? ? printf("V2.0");

#else

? ? printf("未知");

#endif

5、#ifdef 和 #ifndef 指令

相對于 #if 指令(檢測表達式的值是否為真)白嘁,#ifdef 和 #ifndef 指令用于檢測指令關鍵字后面的宏名稱是否已經(jīng)定義坑鱼。其中,#ifdef 指令表示如果宏已經(jīng)被定義絮缅,那么它的檢測結(jié)果為真鲁沥,否則返回假;而 #ifndef 指令的含義正好與 #ifdef 指令相反耕魄,它表示如果宏未被定義画恰,那么它的檢測結(jié)果為真,否則為假吸奴。

在一般情況下允扇,條件編譯指令組合主要有如下幾種形式。

1) 第一種形式如下:

#ifdef 宏名稱

? ? /*程序段1*/

#else

? ? /*程序段2*/

#endif

它表示如果宏名稱已經(jīng)定義则奥,則對程序段 1 進行編譯考润;否則對程序段 2 進行編譯。如果沒有程序段 2读处,則可以省略“#else”糊治,如下所示:

#ifdef 宏名稱

? ? /*程序段1*/

#endif

2) 第二種形式如下:

#ifndef 宏名稱

? ? /*程序段1*/

#else

? ? /*程序段2*/

#endif

#ifndef 指令的含義正好與 #ifdef 指令相反,因此它表示如果宏名稱未被定義档泽,則對程序段 1 進行編譯俊戳,否則對程序段 2 進行編譯。

3) 第三種形式如下:

#if 表達式

? ? /*程序段1*/

#else

? ? /*程序段2*/

#endif

它表示如果表達式的值為真(非 0)馆匿,則對程序段 1 進行編譯抑胎,否則對程序段 2 進行編譯。如果有多個(兩個以上)條件渐北,則可以用 #elif 指令阿逃,如下所示:

#if 表達式

? ? /*程序段1*/

#elif 表達式

? ? /*程序段2*/

#elif 表達式

? ? /*程序段3*/

#else

? ? /*程序段4*/

#endif

妙用“defined”

在 C 語言中,除了“#ifdef”和“#ifndef”指令之外赃蛛,還可以使用 defined 判斷標識符是否定義過恃锉。實際上,“#if defined”等價于“#ifdef”呕臂,而“#if破托!defined”等價于“#ifndef”歧蒋。例如州既,下面的示例代碼就演示了如何使用 defined 來避免重復包含頭文件引起的重復定義問題:

#if !defined(_CASHREGISTER_XML_PARSER_H_)

#define _CASHREGISTER_XML_PARSER_H_

#endif

它實際上等價于下面的代碼:

#ifndef _CASHREGISTER_XML_PARSER_H_

#define _CASHREGISTER_XML_PARSER_H_

#endif

當然吴叶,從上面的示例來看蚌卤,或許看不出來使用 defined 有任何的優(yōu)勢逊彭。但是诫龙,在處理雙重和多重判斷時签赃,defined 的優(yōu)勢就顯現(xiàn)出來了分尸,如下面的代碼所示:

#ifndef _CASHREGISTER_XML_PARSER_H_

#ifndef _CASHREGISTER_XML_TRANSFORM_H_

#ifndef _CASHREGISTER_XML_DECODE_H_

#endif

#endif

#endif

從上面的代碼中可以看出箩绍,使用“#ifdef”和“#ifndef”指令一次只能同時檢測一個宏是否定義。如果需要檢測多個宏圆到,則需將“#ifdef”和“#ifndef”指令重復復制多次芽淡,這樣看起來很不友好挣菲。但是白胀,如果這里使用 defined或杠,那就簡單多了向抢,如下面的代碼所示:

#if !defined(_CASHREGISTER_XML_PARSER_H_)&&!defined(_CASHREGISTER_XML_TRANSFORM_H_)&&!defined(_CASHREGISTER_XML_DECODE_H_)

#endif

其實笋额,defined 的使用是非常普及的兄猩,在一些常見的 C 語言標準庫中也隨處可見枢冤,例如:

#if (!defined __STRICT_ANSI__ && !defined _ISOC99_SOURCE && \

? ? !defined _POSIX_SOURCE && !defined _POSIX_C_SOURCE && \

? ? !defined _XOPEN_SOURCE && !defined _BSD_SOURCE && \

? ? !defined _SVID_SOURCE)

# define _BSD_SOURCE? ? 1

# define _SVID_SOURCE? ? 1

#endif

因此淹真,這里建議使用 defined核蘸。因為即使當前代碼使用的是簡單的條件編譯啸驯,以后在維護或升級時也可能會增加罚斗,這樣也可以提高程序的可維護性针姿。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末距淫,一起剝皮案震驚了整個濱河市榕暇,隨后出現(xiàn)的幾起案子拐揭,更是在濱河造成了極大的恐慌堂污,老刑警劉巖盟猖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡夕玩,警方通過查閱死者的電腦和手機燎孟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爆侣,“玉大人幢妄,你說我怎么就攤上這事斋陪≈孟牛” “怎么了衍锚?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵度宦,是天一觀的道長戈抄。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么嫂用? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任埂蕊,我火速辦了婚禮粒梦,結(jié)果婚禮上匀们,老公的妹妹穿的比我還像新娘泄朴。我一直安慰自己,他們只是感情好局扶,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著畴蒲,像睡著了一般模燥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纠吴,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音伐坏,去河邊找鬼桦沉。 笑死,一個胖子當著我的面吹牛埠褪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渴语,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼掷酗,長吁一口氣:“原來是場噩夢啊……” “哼汇在!你這毒婦竟也來了亩鬼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粤蝎,沒想到半個月后虑凛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體延柠,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡贞间,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盈罐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榜跌。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡闪唆,死狀恐怖盅粪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悄蕾,我是刑警寧澤票顾,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站帆调,受9級特大地震影響奠骄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜番刊,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一含鳞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芹务,春花似錦蝉绷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至佳晶,卻和暖如春桅狠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工中跌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咨堤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓晒他,卻偏偏與公主長得像吱型,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子陨仅,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容