在看ldlm的代码过程当中遇到了一个很奇怪的问题,有不少宏定义使用了do while(0)这种看起来好像没啥用的代码。而后我就问问师兄,才得知,这种用法很常见,本身又查了一下资料,原来在linux内核代码中常常用到这个东西!如今就将这个东西整理一下。html
为何在内核中碰到不少 #define ... do{...} while(0) ?linux
有如下几点缘由:
性能
一、空语句在编译的时候会出现警告,因此有必要用#define FOO do{ } while(0)优化
二、给定一个基本块,能够在里面定义局部变量spa
三、为了可以在条件语句中使用复杂的宏定义。例以下面这段代码:.net
#define FOO(x) \htm
printf("arg is %s\n", x); \blog
do_something_useful(x);get
若是这样用:编译器
if (blah == 2)
F00(blah);
将会被展开为:
if (blah == 2)
printf("arg is %s\n", blah);
do_something_useful(blah);
这样,if条件以后包含了printf()语句,而do_something_useful()调用不能按照预期那样工做。而是用do {...} while(0)定义后,就会展开成如下语句:
if (blah == 2)
do{
printf("arg is %s\n", blah);
do_something_useful(blah);
}while(0);
这是咱们所指望的。
若是你但愿定义一个包含多行语句和一些局部变量的时候. 通常的定义方式只能这样:
#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }
然而在某些状况下,这样并不能正常工做. 下面是包含两个分支的if语句:
if (x > y)
exch(x,y); // Branch 1
else
do_something(); // Branch 2
但这样却只能展开成单分支的if语句,以下:
if (x > y) { // 单分支if
int tmp;
tmp = x;
x = y;
y = tmp;
}
; // 空语句
else // 错误!!! "parse error before else"
do_something();
问题是因为在语句块后直接加入分号(;)引发的. 解决办法是将语句块放入 do 和 while (0)中间.这样就获得了一条单语句, 而不是被编译器判断为语句块.如今的if语句以下:
if (x > y)
do {
int tmp;
tmp = x;
x = y;
y = tmp;
} while(0);
else
do_something();
假设有这样一个宏定义
#define macro(condition) if(condition) dosomething();
如今在程序中这样使用这个宏:
if(temp)
macro(i);
else
doanotherthing();
一切看起来很正常,可是仔细想一想。这个宏会展开成:
if(temp)
if(condition) dosomething();
else
doanotherthing();
这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译经过了,可是运行的结果必定是错误的。
为了不这个错误,咱们使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时由于绝大多数的编译器都可以识别do{…}while(0)这种无用的循环并进行优化,因此使用这种方法也不会致使程序的性能下降。
参考资料