本文章借助視頻與個人所學
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核蘸。因為即使當前代碼使用的是簡單的條件編譯啸驯,以后在維護或升級時也可能會增加罚斗,這樣也可以提高程序的可維護性针姿。