使用條件宏進行條件編譯
譬如,對於同一份代碼,我想編譯出兩個不同的版本,在其中一個版本中去掉某一部分功能,
這時可以通過條件宏判斷是否編譯,例:
如果不使用條件宏進行控制,想編譯兩個不同版本的程序,就需要保存兩份源代碼。
條件編譯的語法和if else語法類似,必須以#endif結尾例如:
#if 常量表達式 //代碼1 #elif 常量表達式 //代碼2 #elif 常量表達式 //代碼3 #else //代碼4 #endif
條件編譯時將會根據常量表達式來求值,如果常量表達式的值為假,那么對應的代碼將不會被 編譯,還可以判斷某個宏是否定義,例如:
#ifdef(NameOfMacro) //代碼1 #else //代碼2 #endif
如果NameOfMacro被定義,那么代碼1將會參與編譯,否則代碼2參與編譯,如下寫法也是等價的:
#if defined(NameOfMacro) //代碼1 #else //代碼2 #endif
#if !defined(NameOfMacro) //代碼2 #else //代碼1 #endif
宏一般都定義在頭文件中,要想使用改宏則使用#include包含該頭文件即可,也可以定義在源文件中,但是這種情況比較少,一定要確保條件判斷中的宏的定義位置位於條件判斷之前,否則不會生效;宏的作用范圍從定義它的位置開始到源文件結束,在其他源文件中該宏不可見,如果想使用某個宏,則必須使用#include指令包含對應的頭文件。
除了在頭文件和源文件中定義宏,還可以在定義預處理宏,
例如:在VS項目屬性中
預處理宏只能是空的宏,但預處理宏作用於整個工程,對該工程中任意源文件可見,優先於任何定義在源碼中的宏.
使用空宏進行說明
可以使用空宏對函數參數進行說明,微軟的API中就使用了空宏,例如:
WINBASEAPI BOOL WINAPI WriteFileEx( _In_ HANDLE hFile, _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
In,_Inout_定義如下:
_In_表示該參數為傳入參數,Inout_表改參數為傳入傳出參數,為了防止_In,_Inout_被
別人定了,所以先使用#ifdef判斷該宏是否被定義,如果被定義則使用#undef取消定義,然后
在重新定義該宏。
#和##運算符
除了#define 標識符 記號序列 這種形式定義宏,還可以定義帶有參數的宏,形式如下:
#define 標識符(標識符列表(可選)) 記號序列,
在定義帶有形參的宏定義中,#和##將會影響宏替換的過程,如果記號序列中某一個參數前
面有一個#,那么替換時將會在這個參數前后插入",將其變成一個字符串,即使該參數是
另一個帶參宏,也不會在進行宏展開替換;如果該參數為一個字符串常量,那么預處理器會
分別為字符串前后的"號進行轉義,將"變成字符,然后在前后各插入一個";
例:
#define TEST0(p1) #p1"333" #define TEST(p1,p2,p3) #p1#p2 #p3 int main() { int nTest1; int nTest2; int nTest3; printf("%s", TEST(TEST0(0),"333",nTest3)); return 0; }
運行結果:
從上面的例子可以看出#運算符主要作用就是將宏定義中的參數變成字符串,而##運算符的
作用就是將宏定義中相鄰的兩個的參數連接在一起組成一個標識符,如果該標識符不符合C
語言表示符的組成規則,那么編譯時會報錯;
例如:
對於使用了##和#的宏定義,預處理器需要進行反復掃描分析記號序列,以此來查找已定義的
標識符進行替換,如果一個表示符在某次掃描中被替換后,再次掃描遇到此標識符時,則不在
進行替換。
例:
#define TEST(p1,p2) p1##p2 int main() { int nTest1; int nTest2; int nTest3; int TEST(TEST(nTest1, nTest2), nTest3) = 3; printf("%d", TEST(TEST(nTest1, nTest2), nTest3)); return 0; }
使用/P命令查看編譯預處理后的結果:
int main() { int nTest1; int nTest2; int nTest3; int TEST(nTest1, nTest2)nTest3 = 3; printf("%d", TEST(nTest1, nTest2)nTest3); return 0; }
可以看出TEST(TEST(nTest1, nTest2), nTest3)第一次展開后為TEST(nTest1, nTest2)nTest3,
然后又遇到了TEST宏定義,則不在進行展開,直接將TEST(nTest1, nTest2)nTest3作為最終結果,但是
TEST(nTest1, nTest2)nTest3顯然不符合C語言表示符的定義,所以編譯時會報錯。
注意:/P命令使用完成后,必須從命令行中刪除它,否則編譯的時候會報找不到xxxxx.obj錯誤
#和##運算符是C語言宏定義中兩個比較有創造力的運算符,可以創造出許多"黑魔法"宏定義
預定義的宏
C語言中有些預定義的宏,這些宏不能取消定義和重定義: