目录
- 背景
- do…while(0)形式宏定义使得宏定义拥有一些函数的特点
- 分析
- 1. 封装:使得宏定义可以包含复杂的内容而不容易出错,提高代码健壮性
- 2. 生命周期:宏定义内/外部定义的同名变量不会冲突
- 3. 返回:提供类似函数return的出口,宏定义逻辑更为灵活清晰
- 总结
背景
最近这段时间都忙着使用QT环境写C++的项目,最近终于有时间复习嵌入式的项目。在浏览某些功能库代码时发现一个很有意思的技巧:do…while(0)形式宏定义,形式如下:
// do...while(0)形式宏定义#define FUN_DRINK do{find_bottle();\ fill_water();\ drink_water();}while(0)
刚开始看到这样的宏定义的时候的时候有点懵,do…while(0)执行一次不就退出循环了嘛,有啥用呢。直接用一般的形式就好了:
// 相对普遍的一般宏定义#define FUN_DRINK find_bottle();\ fill_water();\ drink_water()
于是花了点时间看了一些资料,算是明白了。
基于个人理解的总结:
do…while(0)形式宏定义使得宏定义拥有一些函数的特点
分析
使用do…while(0)形式的后,宏定义便会有以下的特点:
1. 封装:使得宏定义可以包含复杂的内容而不容易出错,提高代码健壮性
当宏定义中包含相对复杂的内容时,常用的宏定义形式为:
// 一般形式#define FUN_DRINK find_bottle();\ fill_water();\ drink_water()// 外加大括号形式#define FUN_DRINK {find_bottle();\ fill_water();\ drink_water();}
如果需要在条件判断语句中使用以上宏定义,代码的未展开/展开的形式分别为
// 未展开if (I_AM_THRISTY)FUN_DRINK;elseFUN_RUN;// 一般形式展开if (I_AM_THRISTY)find_bottle();fill_water();drink_water();else...; // 省略// 外加大括号形式展开if (I_AM_THRISTY){find_bottle();fill_water();drink_water();};else...; // 省略
很显然,以上语句都是会报错的,不是我们预期的结果。而使用do…while(0)形式的宏定义后,代码展开变成了
// 展开if (I_AM_THRISTY)do{find_bottle();fill_water();drink_water();}while(0);else...; // 省略
这样的代码可以正常运行,do…while(0)被看作完整语句块,整体执行,不容易出错,这就类似于函数的封装
宏定义可以包含多个语句块或者是多个其他宏定义嵌套,但如果内容过于复杂的话最好还是通过函数实现
2. 生命周期:宏定义内/外部定义的同名变量不会冲突
如果宏定义中包含了一些变量的定义,而恰好宏定义外部也有同名变量的定义的话,是会出现重定义错误的
// 一般形式#define ADD_OFFSET(a) int offset=50;a+=offset;// 未展开int offset = 0;int a = 300;ADD_OFFSET(a);printf("offset = %d", offset);printf("a = %d", a);// 展开int offset = 0;int a = 300;int offset=50; // 重定义错误a+=offset;printf("offset = %d", offset);printf("a = %d", a);
使用do…while(0)形式的宏定义后展开变成了
int offset = 0;int a = 300;do{int offset=50;a+=offset;}while(0)printf("offset = %d", offset);printf("a = %d", a);
很显然,内部变量的生命周期到循环结束就销毁了,作用域也只在do…while(0)循环体内,不会影响到外部同名变量的定义,这就类似于函数内部变量的生命周期
3. 返回:提供类似函数return的出口,宏定义逻辑更为灵活清晰
这一点,是利用了break语句可以退出循环体的特性。举例如下:
#define MULTI_TASK(choice) do{if(choice==0){fun0();break;}; if(choice==1){fun1();break;}; ...}while(0)// 未展开MULTI_TASK(0);MULTI_TASK(1);// 展开do{if(0==0){fun0();break;}; if(0==1) { fun1(); break; };...}while(0);do{if(1==0){fun0();break;}; if(1==1) { fun1(); break; };...}while(0);
通过break退出循环,相当于给代码提供了一个灵活的出口,使得宏定义中的内容可以被有选择地运行,而不用一股脑地全部运行直到while(0)之后退出,这就类似于函数的return
总结
虽然很多书籍/教材都不提倡用宏定义,原因是宏定义如果用不好可能会引发更多的麻烦,并且Debug的时候也不太好找,导致很多Coder对宏定义都避而远之
但是宏定义实际上如果运用的好的话,是一个非常好用的工具,说到底还是个人能力问题(菜是原罪),有能力还是要多学习,毕竟技多不压身嘛