最近在看esp32的idf,有一些宏定义使用了do while(0)这种看起来好像没啥用的代码。然后我查了一下资料,发现在linux内核代码中经常用到这个东西!
现在就将这个东西整理一下。
为什么在内核中碰到很多
#define ... do{...} while(0)
有以下几点原因:
1、空语句在编译的时候会出现警告,所以有必要用#define FOO do{ } while(0)
2、给定一个基本块,可以在里面定义局部变量
3、为了能够在条件语句中使用复杂的宏定义。例如下面这段代码:
#define FOO(x) printf("arg is %s ", x); do_something_useful(x);
如果这样用:
if (blah == 2) F00(blah);
将会被展开为:
if (blah == 2) printf("arg is %s ", blah); do_something_useful(blah);
这样,if条件之后包含了printf()语句,而do_something_useful()调用不能按照预期那样工作。
而用do {...} while(0)定义后,就会展开成以下语句:
if (blah == 2) do{ printf("arg is %s ", 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) do_something();
现在在程序中这样使用这个宏:
if(temp) macro(i); else do_anotherthing();
一切看起来很正常,但是仔细想想。这个宏会展开成:
if(temp) if(condition) do_something(); else do_anotherthing();
这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。
为了避免这个错误,我们使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。
参考资料