源码

在AliOS-Things中的k_task.c文件中我们可以发现有这样一个函数调用NULL_PARA_CHK(task)。其功能是检查task变量是否为NULL值,完整的函数定义如下。

1
2
3
4
5
6
#define NULL_PARA_CHK(para)        \
do { \
if (para == NULL) { \
return RHINO_NULL_PTR; \
} \
} while (0)

可以看到该函数就是一个宏定义,那么问题就来了。这样做有什么好处吗,为什么不直接使用大括号呢。

用处

这是一个c语言可以使用的构造函数,其在后面加分号也没问题,在if后面加该宏也没问题。这样说可能有点难理解,还是用案例来说比较好。

假设我们有一个宏定义如下

1
#define f(x) foo(x);bar(x)

如果我们如以下方法调用时不会有问题

1
2
3
f(x);
// 实际
foo(x);bar(x);

但是如果我们放到if语句里面就会产生问题

1
2
3
4
5
6
if (cond)
f(x);
// 实际
if (cond)
foo(x);
bar(x);

可以看到if后面使用该宏的时候,bar(x)在if外面显然不是我们想要的。如果我们用大括号括起来呢?重新定义宏如下

1
#define f(x) {foo(x);bar(x);}

显然放到了上面的if环境中没有问题,但是如果我们再加一个else就有问题了。

1
2
3
4
5
6
7
8
9
10
11
12
if (cond)
f(x);
else
other();
// 实际
if (cond)
{
foo(x);
bar(x);
};
else
other();

可以发现在大括号后面多了一个分号,在该情况下会有语法问题。因此我们重新修改宏定义如下。

1
#define f(x) do{foo(x);bar(x);}while(0)

在do…while(0)宏定义中,do…while语句保证了do中的代码一定会被执行一次,而while(0)保证其只被执行一次。且使用这种宏在上述的if else语句中也不会出错。也可以在后面加上分号。

1
2
3
4
5
6
7
8
9
10
11
12
if(cond)
f(x);
else
other();
// 实际
if(cond)
do{
foo(x);
bar(x);
}while(0);
else
other();

后记

了解到了c语言宏定义一种神奇的使用方法,同时也了解到了do…while语句的一个用处。知识up。

参考资料

Why use apparently meaningless do-while and if-else statements in macros?