嵌入式宏定义中do...while(0)的意义

背景

最近这段时间都忙着使用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;
else
	FUN_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对宏定义都避而远之

但是宏定义实际上如果运用的好的话,是一个非常好用的工具,说到底还是个人能力问题(菜是原罪),有能力还是要多学习,毕竟技多不压身嘛

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
嵌入式C语言八股文是指在嵌入式系统开发常见的基本知识点和技能要求的简要总结。下面是嵌入式C语言八股文的主要内容: 1. 数据类型:包括基本数据类型(如int、char、float等)和派生数据类型(如数组、结构体、枚举等),掌握各种数据类型的使用方法和特点。 2. 运算符:熟悉各种算术运算符、逻辑运算符、位运算符等,掌握它们的优先级和结合性,能够正确使用运算符完成各种计算任务。 3. 控制语句:包括条件语句(if-else语句)、循环语句(for、while、do-while循环)、选择语句(switch-case语句)等,掌握这些语句的使用方法和注意事项。 4. 函数:了解函数的定义和调用,能够编写函数并正确使用函数参数和返回值,理解函数的作用域和生命周期。 5. 数组和指针:掌握数组和指针的定义和使用,了解数组和指针在内存的存储方式,能够通过指针进行数组的访问和操作。 6. 文件操作:了解文件操作的基本流程,包括文件的打开、读写和关闭,理解文件指针和文件访问模式的概念。 7. 断处理:了解断的基本概念和原理,能够编写断服务程序(ISR)并正确处理断请求。 8. 程序调试:掌握常用的调试技巧和工具,能够使用调试器进行程序的单步执行、观察变量值等操作,能够分析程序运行过程的错误和异常。 以上是嵌入式C语言八股文的主要内容,掌握这些知识和技能,可以帮助你在嵌入式系统开发更好地应对各种任务和挑战。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值