有些程序在調試、兼容性、平臺移植等狀況下可能想要經過簡單地設置一些參數就生成一個不一樣的軟件,這固然能夠經過變量設置,把全部可能用到的代碼都寫進去,在初始化時配置,但在不一樣的狀況下可能只用到一部分代碼,就不必把全部的代碼都寫進去,就能夠用條件編譯,經過預編譯指令設置編譯條件,在不一樣的須要時編譯不一樣的代碼。函數
(一)條件編譯方法spa
條件編譯是經過預編譯指令來實現的,主要方法有:調試
一、#if, #elif, #else, #endifcode
#if 條件 1
字符串
代碼段 1
#elif 條件 2
代碼段 2
...
#elif 條件 n
代碼段 n
#else
代碼段 n+1
#endif
便可以設置不一樣的條件,在編譯時編譯不一樣的代碼,預編譯指令中的表達式與C語言自己的表達式基本一至如邏輯運算、算術運算、位運算等都可以在預編譯指令中使用。之因此可以實現條件編譯是由於預編譯指令是在編譯以前進行處理的,經過預編譯進行宏替換、條件選擇代碼段,而後生成最後的待編譯代碼,最後進行編譯。編譯器
#if的通常含義是,若是#if後面的常量表達式爲true,則編譯它所控制的代碼,如條件1成立時就代碼段1,條件1不成立再看條件2是否成立,若是條件2成立則編譯代碼段2,不然再依次類推判斷其它條件,若是條件1-N都不成力則會編譯最後的代碼段n+1.it
二、#ifdef, #else, #endif或#ifndef, #else, #endif編譯
條件編譯的另外一種方法是用#ifdef與#ifndef命令,它們分別表示「若是有定義」及「若是無定義」。有定義是指在編譯此段代碼時是否有某個宏經過 #define 指令定義的宏,#ifndef指令指找不到經過#define定義的某宏,該宏能夠是在當前文件此條指令的關面定義的,也能夠是在其它文件中,但在此指令以前包含到該文件中的。class
#ifdef的通常形式是:變量
#ifdef macro_name
代碼段 1
#else
代碼段 2
#endif
或
#ifdef的通常形式是:
#ifndef macro_name
代碼段 2
#else
代碼段 1
#endif
這兩段代碼的效果是徹底同樣的。
三、經過宏函數defined(macro_name)
參數爲宏名(無需加""),若是該macro_name定義過則返回真,不然返回假,用該函數則能夠寫比較複雜的條件編譯指令如
#if defined(macro1) || (!defined(macro2) && defined(macro3))
...
#else
...
#endif
(二)條件編譯技巧與示例
(1)#ifdef和#defined()比較
首先比較一下這兩種方法,第一種方法只能判斷一個宏,若是條件比較複雜實現起來比較煩鎖,用後者就比較方便。若有兩個宏MACRO_1,MACRO_2只有兩個宏都定義過纔會編譯代碼段A,分別實現以下:
#ifdef MACRO_1
#ifdef MACRO_2
代碼段 A
#endif
#endif
或者
#if defined(MACRO_1) && defined(MACRO_2)
#endif
一樣,要實現更復雜的條件用#ifdef更麻煩,因此推薦使用後者,由於即便當前代碼用的是簡單的條件編譯,之後在維護、升級時可能會增長,用後者可維護性較強。舊的編譯器可能沒有實現#defined()指令,C99已經加爲標準。要兼容老的編譯器,還需用#ifdef指令。
二、#if與 #ifdef或#if defined()比較
好比本身寫了一個printf函數,想經過一個宏MY_PRINTF_EN實現條件編譯,用#if可實現以下
#define MY_PRINTF_EN 1
#if MYS_PRINTF_EN == 1
int printf(char* fmt, char* args, ...)
{
...
}
#endif
若是宏MY_PRINTF_EN定義爲1則編譯這段代碼,若是宏定義不爲1或者沒有定義該宏,則不編譯這段代碼。一樣也能夠經過#ifdef或者#defined()實現,如
#define MY_PRINTF_EN 1
#if defined(MY_PRINTF_EN)
int printf(char* fmt, char* args, ...)
{
...
}
#endif
在這種狀況下兩種方法具備殊途同歸之妙,但試想若是你爲了節約代碼寫了兩個printf函數,在不一樣狀況下使用不一樣的printf函數,一個是精簡版一個是全功能標準版,如:
#define MY_PRINTF_SIMPLE
#ifdef MY_PRINTF_SIMPLE
void printf(*str) // 向終端簡單地輸出一個字符串
{...
}
#endif
#ifdef MY_PRINTF_STANDARD
int printf(char* fmt, char* args, ...)
{...
}
#endif
一樣能夠用#if defined()實現
#define MY_PRINTF_SIMPLE
#if defined(MY_PRINTF_SIMPLE)
void printf(*str) // 向終端簡單地輸出一個字符串
{
...
}
#elif defined(MY_PRINTF_STANDARD)
int printf(char* fmt, char* args, ...)
{
...
}
#endif
兩種方法均可以實現,但可見後者更方便。但試想若是你有三個版本,用前者就更麻煩了,但方法類似,用後者就更方便,但仍需三個宏進行控制,你要住三個宏,改進一下就用#if能夠用一個宏直接控制N種狀況如:
#define MY_PRINTF_VERSION 1
#if MY_PRINTF_VERSION == 1
void printf(*str) // 向終端簡單地輸出一個字符串
{
...
}
#elif MY_PRINTF_VERSION == 2
int printf(char* fmt, char* args, ...)
{
...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
...
}
#else
默認版本
#endif
這樣,你只需修改一下數字就能夠完成版本的選擇了
看來好像用#if 比較好了,試想以下狀況:你寫了一個配置文件叫作config.h用來配置一些宏,經過這些宏來控制代碼,如你在config.h的宏
#define MY_PRINTF_EN 1
來控制是否須要編譯本身的printf函數,而在你的源代碼文件printf.c中有以下指令
#i nclude "config.h"
#if MY_PRINTF_EN == 1
int printf(char* fmt, char* args, ...)
{
...
}
#endif
但這樣也會有一個問題,就是若是你忘了在config.h中添加宏MY_PRINTF_EN,那麼本身寫的printf函數也不會被編譯,有些編譯器會給出警告:MY_PRINTF_EN未定義。若是你有兩個版本的想有一個默認版本,能夠在printf.c中這樣實現
#incldue "config.h"
#if !defined(MY_PRINTF_VERSION)
#define MY_PRINTF_VERSION 1
#endif
#if MY_PRINTF_VERSION == 1
void printf(*str) // 向終端簡單地輸出一個字符串
{
...
}
#elif MY_PRINTF_VERSION == 2
int printf(char* fmt, char* args, ...)
{
...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
...
}
#endif
這種狀況下還得用到#ifdef或#if defined(),你能夠不用動主體的任何代碼,只須要修改printf.c文件中MY_RPINTF_VERSION宏的數字就能夠改變了,若是用前面那種方法還得拖動代碼,在拖動中就有可能形成錯誤。
再試想,若是軟件升級了,或者有了大的改動,原來有三個版本,如今只剩下兩個版本了,如
#if MY_PRINTF_VERSION == 2
int printf(char* fmt, char* args, ...)
{
...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
...
}
#endif
由於這些核心代碼不想讓使用這些代碼的人關心,他們只須要修改config.h文件,那就要在printf.c中實現兼容性。若是之前有人在config.h配置宏MY_PRINTF_VERSION爲1,即有
#define MY_PRINTF_VERSION 1
而如今沒有1版本了,要想兼容怎麼辦?那固然能夠用更復雜的條件實現如:
#if MY_PRINTF_VERSION == 2 || MY_PRINTF_VERSION == 1
int printf(char* fmt, char* args, ...)
{
...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
...
}
#endif
不過還有另一種方法,即便用#undef命令
#if MY_PRINTF_VERSION == 1
#undef MY_PRINTF_VERSION
#define MY_PRINTF_VERSION 2
#endif
#if MY_PRINTF_VERSION == 2
int printf(char* fmt, char* args, ...)
{
...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
...
}
#endif
用#if還有一個好處,若是你把宏名記錯了,把MY_PRINTF_EN定義成了MY_PRINT_EN,那麼你用#ifdef MY_PRINTF_EN或者#if defined(MY_PRINTF_EN)控制的代碼就不能被編譯,查起來又很差查,用#if MY_PRINTF_EN ==1控制就很好查,由於你把MY_PRINTF_EN定義成MY_PRINT_EN,則MY_PRINTF_EN實際上沒有定義,那麼編譯器會給出警告#if MY_PRINTF_EN == 1中的MY_PRINTF_EN沒有定義,但錯就比較快。