宏定义中的do...while(0)用途


最近在考哪代码时经常碰到do...while(0)这种「奇怪」的定义方式,例如:
#define ComConfigOpStatsResetDownload(opState)
do {
(opState) &= ~(ComConfigOpStateDownload);
} while(0)
看到之后很疑惑,为什么这样写呢那个?岂不是多次一举。之后仔细一想,确实有深意,结合后来查阅资料,总结这样写有以下几点好处:

  • 避免if-else代码块匹配错误。如果不使用do...while(0),如下
    #define PRINT_LOG(str)
    if(DEBUG)
    NSLog(str)

    那么如果调用该宏时如下:
    if(message == nil)
    PRINT_LOG(@"message is nil");
    else
    [label setText:message];

    看起来貌似没问题,但是事实上宏定义展开后就成为了:
    if(message == nil)
    if(DEBUG)
    NSLog(@"message is nil");
    else
    [label setText:message];
    很明显,else没有匹配到我们想匹配的地方。而这种问题又是很难发现的,确实比较坑。当然,我们如果用do...while(0)将宏定义包裹起来就不会有这样的问题了,使用do...while(0)包裹后展开代码将会是:
    if(message == nil)
    do{
    if(DEBUG)
    NSLog(@"message is nil");
    }while(0);
    else
    [label setText:message];
    是不是很cool。哈哈。

  • 避免上下文匹配错误。其实和第一点类似,举个栗子:
    #define SWITCH_A_B(A,B) int temp = B;B = A;A = temp;
    调用时候如下:
    if(isNeedSwitch)
    SWITCH_A_B(numA, numB);
    宏展开之后就是:
    if(isNeedSwitch)
    int temp = B;
    B = A;
    A = temp;;
    匹配错误!如果使用do...while(0)定义即可解决。
    看了上面两点,如果你是一个善于思考的boy(or girl),那么你可能要说,为什么不用{}来包裹呢?如果用{}包裹,也会出现上下文混淆,例如下面:

     #define SWITCH_A_B(A,B) {int temp = B;B = A;A = temp;}
    

    那么,假如我的调用部分是这样:
    if(isNeedSwitch)
    SWITCH_A_B(A, B);
    else
    NSLog(@"its ok"); 看起来没什么问题,展开后又出问题了: if(isNeedSwitch) { int temp = B; B = A; A = temp; }; else NSLog(@"its ok");

    对,没错,多出一个';',直接导致我们编译不过。

  • 上面说到,第二点中代码展开会多出一个';'。没错,do...while(0)的第三个好处就是相当于在宏定义中有一个block(代码块)来盛放大段代码,形成一个语法单元,不会造成上下文混淆,调用时候看起来更像函数调用,不会多出';'。

  • 第四个原因就是我们一些宏可能会是空的,例如
    if(DEBUD)
    #define PRINT_TIME NSLOG(@"time:%@",stime);
    else
    #define PRINT_TIME
    这样可能会出现讨厌的warning,所以,我们就可以写成:
    if(DEBUD)
    #define do{PRINT_TIME NSLOG(@"time:%@",stime);}while(0)
    else
    #define PRINT_TIME do{}while(0)
    如此,我们就轻松解决了warning问题。


综上,在复杂代码块的宏定义以及涉及if判断的宏定义,最好使用do...while(0)包裹起来。同shi,从上面的例子中,你可能也注意到在if-else代码块最好养成使用'{}'的习惯,能够避免上下文混淆问题(这类问题也是相当难查的)。ok,今天就写到这里,希望对你有用,欢迎各位补充指教。

你可能感兴趣的:(宏定义中的do...while(0)用途)