C語言宏的使用


 

使用條件宏進行條件編譯

譬如,對於同一份代碼,我想編譯出兩個不同的版本,在其中一個版本中去掉某一部分功能,
這時可以通過條件宏判斷是否編譯,例:
C語言_宏1.png
如果不使用條件宏進行控制,想編譯兩個不同版本的程序,就需要保存兩份源代碼。

條件編譯的語法和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項目屬性中
C語言_宏2.png
預處理宏只能是空的宏,但預處理宏作用於整個工程,對該工程中任意源文件可見,優先於任何定義在源碼中的宏.

使用空宏進行說明

可以使用空宏對函數參數進行說明,微軟的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_定義如下:
C語言_宏3.png
_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;
 }

運行結果:
帶參宏定義.png

從上面的例子可以看出#運算符主要作用就是將宏定義中的參數變成字符串,而##運算符的
作用就是將宏定義中相鄰的兩個的參數連接在一起組成一個標識符,如果該標識符不符合C
語言表示符的組成規則,那么編譯時會報錯;
例如:
帶參宏定義1.png

對於使用了##和#的宏定義,預處理器需要進行反復掃描分析記號序列,以此來查找已定義的
標識符進行替換,如果一個表示符在某次掃描中被替換后,再次掃描遇到此標識符時,則不在
進行替換。
例:

 #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;
 }

帶參宏定義2.png

使用/P命令查看編譯預處理后的結果:
大P命令.png

 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語言中有些預定義的宏,這些宏不能取消定義和重定義:
C語言標准預定義的宏.png

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM