内核里有很多代码里有类似do{}while(0)
这样的写法,这个写法有两个不同的用法。
本文介绍了do{}while(0)
的两个不同的用法。
1.形成单独逻辑块(避免宏替换以后产生错误)
举个例子:(https://elixir.bootlin.com/linux/v2.6.39.4/source/include/net/udp.h#L211)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#define UDP6_INC_STATS_BH(net, field, is_udplite) do { \
if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_stats_in6, field);\
else SNMP_INC_STATS_BH((net)->mib.udp_stats_in6, field); \
} while(0)
#define UDP6_INC_STATS_USER(net, field, __lite) do { \
if (__lite) SNMP_INC_STATS_USER((net)->mib.udplite_stats_in6, field); \
else SNMP_INC_STATS_USER((net)->mib.udp_stats_in6, field); \
} while(0)
#define UDPX_INC_STATS_BH(sk, field) \
do { \
if ((sk)->sk_family == AF_INET) \
UDP_INC_STATS_BH(sock_net(sk), field, 0); \
else \
UDP6_INC_STATS_BH(sock_net(sk), field, 0); \
} while (0);
|
先看前两个宏用了do {…} while(0) ,如果展开到第三个宏就是:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#define UDPX_INC_STATS_BH(sk, field) \
do { \
if ((sk)->sk_family == AF_INET) \
do { \
if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_stats_in6, field);\
else SNMP_INC_STATS_BH((net)->mib.udp_stats_in6, field); \
} while(0);
else \
do { \
if (__lite) SNMP_INC_STATS_USER((net)->mib.udplite_stats_in6, field); \
else SNMP_INC_STATS_USER((net)->mib.udp_stats_in6, field); \
} while(0);
} while (0);
|
如果去掉前两个宏的do while去掉, 就变成:
1
2
3
4
5
6
7
8
9
|
#define UDPX_INC_STATS_BH(sk, field) \
do { \
if ((sk)->sk_family == AF_INET) \
if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_stats_in6, field);\
else SNMP_INC_STATS_BH((net)->mib.udp_stats_in6, field); \
else \
if (__lite) SNMP_INC_STATS_USER((net)->mib.udplite_stats_in6, field); \
else SNMP_INC_STATS_USER((net)->mib.udp_stats_in6, field); \
} while (0);
|
显而易见,else无法匹配,代码完全错乱,无法编译通过。
所以一般只要在宏定义中含有逻辑判断的情况,都要用do while去分割,这样比较安全,再修改代码也不需要会过头去重新匹配if else 等。
2. 精简判断分支(不使用goto语句)
比如如下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
bool Execute()
{
// 分配资源
int *p = new int;
bool bOk(true);// 执行并进行错误处理
bOk = func1();
if(!bOk) goto errorhandle;
bOk = func2();
if(!bOk) goto errorhandle;
bOk = func3();
if(!bOk) goto errorhandle;
// ..........
// 执行成功,释放资源并返回
delete p;
p = NULL;
return true;
errorhandle:
delete p;
p = NULL;
return false;
}
|
虽然正确的使用goto
可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto
语句,又能消除代码冗余呢,请看do…while(0)
循环:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
bool Execute()
{
// 分配资源
int *p = new int;bool bOk(true);
do
{
// 执行并进行错误处理
bOk = func1();
if(!bOk) break;
bOk = func2();
if(!bOk) break;
bOk = func3();
if(!bOk) break;
// ..........
}while(0);
// 释放资源
delete p;
p = NULL;
return bOk;
}
|
参考文章
Author
laoqinren
LastMod
2020-08-03
Markdown
The Markdown version »