C語言中do...while(0)的妙用


在linux內核代碼中,經常看到do...while(0)的宏,do...while(0)有很多作用,下面舉出幾個:

1、避免goto語句:

通常,如果一個函數開始要分配一些資源,然后如果在中途遇到錯誤則要退出函數,當然,退出前要釋放資源,我們的代碼可能如下: 

 1 #defien N 10
 2 
 3 bool Execute()
 4 {
 5    // 分配資源
 6    int *p = (int *)malloc(N * sizeof(int));
 7    bool bOk = true;
 8 
 9    // 執行並進行錯誤處理
10    bOk = func1();
11    if(!bOk) 
12    {
13       free(p);   
14       p = NULL;
15       return false;
16    }
17 
18    bOk = func2();
19    if(!bOk) 
20    {
21       free(p);   
22       p = NULL;
23       return false;
24    }
25 
26    bOk = func3();
27    if(!bOk) 
28    {
29       free(p);    
30       p = NULL;
31       return false;
32    }
33 
34    // ..........
35 
36    // 執行成功,釋放資源並返回
37     free(p);   
38     p = NULL;
39     return true;
40 }
C代碼

這里最大的問題是代碼冗余,每增加一個操作,就要做相應的錯誤處理,非常不靈活,於是想到了一下的goto:

 1 #defien N 10
 2 
 3 bool Execute()
 4 {
 5    // 分配資源
 6    int *p = (int *)malloc(N * sizeof(int));
 7    bool bOk = true;
 8 
 9    // 執行並進行錯誤處理
10    bOk = func1();
11    if(!bOk) goto errorhandle;
12 
13    bOk = func2();
14    if(!bOk) goto errorhandle;
15 
16    bOk = func3();
17    if(!bOk) goto errorhandle;
18 
19    // ..........
20 
21    // 執行成功,釋放資源並返回
22     free(p);   
23     p = NULL;
24     return true;
25 
26     errorhandle:
27     free(p);   
28     p = NULL;
29     return false; 
30 }
C代碼

代碼冗余是解決了,但是引入了C語言中比較微妙的goto語句,雖然正確的使用goto語句可以大大提高程序的靈活性與簡潔性,但是會使我們的程序捉摸不定,為了既避免使用goto語句,又能消除代碼冗余,可以考慮使用下面的 do...while(0):

 1 #defien N 10
 2 
 3 bool Execute()
 4 {
 5     //分配資源
 6     int *p = (int *)malloc(N * sizeof(int));
 7     bool bOK = true;
 8 
 9 
10     do {
11         //執行並進行錯誤處理
12         bOK = fun1();
13         if(!bOK) break;
14 
15         bOK = fun2();
16         if(!bOK) break;
17 
18         bOK = fun3();
19         if(!bOK) break;
20 
21         //.........
22     }  while(0);
23 
24     //釋放資源
25     
26     free(p);
27     p = NULL;
28     return bOK;
29 }
C代碼

2、避免空聲明在編譯時出現警告:

在linux內核源代碼中,經常看到如下宏以避免在編譯時出現警告:

#define FOO do { } while(0)

3、提供一個聲明局部變量的基礎塊:

你可能經常會使用如下的宏:

#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }

然而在某些情況下將會失效,下面的代碼使用if...else...

if (x > y)
        exch(x,y);          // 分支 1
else  
        do_something();     // 分支 2

但是將被解釋為一個分支的if語句:

if (x > y) {     
        int tmp;            
        tmp = x;            
        x = y;
        y = tmp;
}
;                           // 空語句
else                        // ERROR!!! 
        do_something();

錯誤出在“;”直接位於代碼塊的后面,解決的辦法是將代碼嵌入do...while(0),於是得到下面的代碼:

1 if (x > y)
2         do {
3                 int tmp;
4                 tmp = x;
5                 x = y;
6                 y = tmp;
7         } while(0);
8 else
9         do_something();

 於是上面的宏可以修改為:

1 #define exch(x,y)       do {\
2                 int tmp;\
3                 tmp = x;\
4                 x = y;\
5                 y = tmp;\
6         } while(0)

4、在條件語句中使用復雜的宏:

假如一個宏包含類似如下幾行代碼:

#define FOO(x) \
        printf("arg is %s\n", x); \
        do_something_useful(x);

現在想像一下下面的代碼:

if (blah == 2)
        FOO(blah);

這將解釋為:

if (blah == 2)
        printf("arg is %s\n", blah);
        do_something_useful(blah);;

我們就會發現,if語句只作用於printf(), do_something_useful() 沒按照願意一起執行,即沒有像你預期的那樣被包含在if代碼中,於是可以使用如下的代碼塊:

if (blah == 2)
        do {
                printf("arg is %s\n", blah);
                do_something_useful(blah);
        } while (0);

這樣上面的宏就可以改為:

1 #define  FOO(x) do { \
2                 printf("arg is %s\n", blah);\
3                 do_something_useful(blah);\
4         } while (0)


PS:以上的第三種和第四種技巧,並不是唯一的方法,有同學留言說用其他的方法也可以實現,反而顯得這樣的宏定義過於花哨?事實並非如此,這樣的宏定義在linux內核代碼中非常常見,原因是代碼簡潔、通用、可移植性好


免責聲明!

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



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