三言两语聊Kernel:do{…}while(0)
在内核代码里我们会看到很多do{…}while(0)来定义的宏,比如(摘自include/asm-generic/barrier.h):
linux kernel里这么定义自然有其特殊的目的。
在C语言里,使用do/while(0)模式来定义的宏在任何情况下都有同样的行为,而且在C语言里面,只有do/while(0)模式来定义的宏才会在任何情况下(比如,在没有“ }”的if语句中)都有同样的行为.
举一些例子。
1
|
|
对于 foo(test);
会被展开为: a(test); b(test);
如果是这样的话,这看起来很正确,没有任何错误,是你原本想要得到的结果。
但如果这样用:
1 2 |
|
它就会被展开为:
1 2 |
|
它的行为就变成了:
1 2 3 |
|
这并不是你原本想要的。
然后,我们重新定义该宏,使用do/while(0)来封装:
1
|
|
这样定义的宏和前面定义的宏具有同样的作用,只不过,do确保了它的整个逻辑都在大括号里面执行,while(0)确保它只执行一次。所以它跟没有do/while(0) 的宏具有同样的效果。那么,他们有什么不同吗?让我们再来看前面那个例子:
1 2 |
|
现在它就变成了:
1 2 |
|
实际上就是:
1 2 3 4 |
|
你可能会疑问,为什么不直接使用“{}”来定义该宏?让我们来看下面这种情况:
1
|
|
那么:
1 2 3 4 |
|
就变成了:
1 2 3 4 5 6 |
|
显然这是一个语法错误,因为else前面没有if。
使用这种方式来定义宏,还有个好处是,在”{…}“里面可以定义自己的局部变量,而不被外面的变量所干扰。
在linux内核代码里,很多宏都是使用do/while(0)来封装的,目的就是为了让它在任何情况下都具有同样的行为。在我们自己的代码里,也要养成这种好习惯。