最近在考哪代码时经常碰到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(@"it
s 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,今天就写到这里,希望对你有用,欢迎各位补充指教。