源码

在 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?