1. 程式人生 > >【C】巨集高階用法

【C】巨集高階用法

1、前言

今天看程式碼時候,遇到一些巨集,之前沒有見過,感覺挺新鮮。如是上網google一下,順便總結一下,方便以後學習和運用。C語言程式中廣泛的使用巨集定義,採用關鍵字define進行定義,巨集只是一種簡單的字串替換,根據是否帶引數分為無參和帶參。巨集的簡單應用很容易掌握,今天主要總結一下巨集的特殊符號及慣用法。

(1)巨集中包含特殊符號:#、##.
(2)巨集定義用do{ }while(0)

2、特殊符號#、##

(1)#

When you put a # before an argument in a preprocessor macro, the preprocessor turns that argument into a character array.

在一個巨集中的引數前面使用一個#,前處理器會把這個引數轉換為一個字元陣列

簡化理解:#是“字串化”的意思,出現在巨集定義中的#是把跟在後面的引數轉換成一個字串

#define ERROR_LOG(module)   fprintf(stderr,"error: "#module"\n")

ERROR_LOG(“add”); 轉換為 fprintf(stderr,“error: “add”\n”);

ERROR_LOG(devied =0); 轉換為 fprintf(stderr,“error: devied=0\n”);

(2)##

“##”是一種分隔連線方式,它的作用是先分隔,然後進行強制連線。

在普通的巨集定義中,前處理器一般把空格解釋成分段標誌,對於每一段和前面比較,相同的就被替換。但是這樣做的結果是,被替換段之間存在一些空格。如果我們不希望出現這些空格,就可以通過新增一些##來替代空格。

1 #define TYPE1(type,name)   type name_##type##_type
2 #define TYPE2(type,name)   type name##_##type##_type

TYPE1(int, c); 轉換為:int  name_int_type ; (因為##號將後面分為 name_ 、type 、 type三組,替換後強制連線)
TYPE2(int, d);轉換為: int  d_int_type ; (因為##號將後面分為 name、

、type 、_type四組,替換後強制連線)

3、巨集定義中do{ }while(0)

第一眼看到這樣的巨集時,覺得非常奇怪,為什麼要用do……while(0)把巨集定義的多條語句括起來?非常想知道這樣定義巨集的好處是什麼,於是google、百度一下了。

採用這種方式是為了防範在使用巨集過程中出現錯誤,主要有如下幾點:

(1)空的巨集定義避免warning:
  #define foo() do{}while(0)
  (2)存在一個獨立的block,可以用來進行變數定義,進行比較複雜的實現。
  (3)如果出現在判斷語句過後的巨集,這樣可以保證作為一個整體來是實現:

    #define foo(x) \
        action1(); \
        action2();

在以下情況下:

if(NULL == pPointer)
         foo();

就會出現action1和action2不會同時被執行的情況,而這顯然不是程式設計的目的。
  (4)以上的第3種情況用單獨的{}也可以實現,但是為什麼一定要一個do{}while(0)呢,看以下程式碼:

#define switch(x,y) {int tmp; tmp="x";x=y;y=tmp;}
      if(x>y)
        switch(x,y);
      else       //error, parse error before else
      otheraction();

在把巨集引入程式碼中,會多出一個分號,從而會報錯。這對這一點,可以將if和else語句用{}括起來,可以避免分號錯誤。
  使用do{….}while(0) 把它包裹起來,成為一個獨立的語法單元,從而不會與上下文發生混淆。同時因為絕大多數的編譯器都能夠識別do{…}while(0)這種無用的迴圈並進行優化,所以使用這種方法也不會導致程式的效能降低

4、測試程式

簡單寫個測試程式,加強練習,熟悉一下巨集的高階用法。

 1 #include <stdio.h>
 2 
 3 #define PRINT1(a,b)        \
 4     {                  \
 5       printf("print a\n"); \
 6       printf("print b\n"); \
 7     }
 8 
 9 #define      PRINT2(a, b)      \
10   do{               \
11       printf("print a\n"); \
12       printf("print b\n"); \
13     }while(0)  
14 
15 #define PRINT(a) \
16     do{\
17     printf("%s: %d\n",#a,a);\
18     printf("%d: %d\n",a,a);\
19     }while(0)
20 
21 #define TYPE1(type,name)   type name_##type##_type
22 #define TYPE2(type,name)   type name##_##type##_type
23 
24 #define ERROR_LOG(module)   fprintf(stderr,"error: "#module"\n")
25 
26  main()
27 {
28     int a = 20;
29     int b = 19;
30     TYPE1(int, c);
31     ERROR_LOG("add");
32     name_int_type = a;
33     TYPE2(int, d);
34     d_int_type = a;
35 
36     PRINT(a);
37     if (a > b)
38     {
39     PRINT1(a, b);
40     }
41     else
42     {
43     PRINT2(a, b);
44     }
45     return 0;
46 }

測試結果如下:

error:"add"
a:20
20:20
print a
print b