你想知道的do{...}while(0)的作用,都在这里了
0、引言
我们在嵌入式开发的过程中,经常可以碰到在一些宏定义或者是代码段中使用了do {...} while(0)的语句,从语义上理解,do {...} while(0)内的逻辑就只执行一次,并没有循环执行,粗略看来,似乎画蛇添足了,那么为什么还需要在只执行一次的逻辑外面加上一层do {...} while(0)语句呢?实际上,在这些逻辑中使用do {...} while(0)的作用远大于美化你的代码,下面就来看看实际的使用场景。
1、用于定义一个作用域,避免替换的时候出错
我们都知道,在程序中如果一些常量参数或者代码语句反复出现,就可以使用宏定义来替代。预处理阶段,对程序中所有出现的“宏名”,预处理器都会用宏定义中的字符串替代,这称为“宏替换”或“宏展开”。
这样做可提高程序的通用性和易读性,减少不一致性,一个较好的宏名可以更好的让读者理解常量参数的含义;同时程序易于修改,我们仅需要改变一个宏定义,就可以改变整个程序中出现的所有该常量或者语句。
但是有时可能程序代码段中,出现多条语句重复连续的使用,这样我们就可以尝试使用一个复杂的宏来替换。你有可能会这样定义:
1 #define REPLACE_FUN() funA(); funB()
本意是在程序中当出现funA()和funB()多条语句连续使用时,使用REPLACE_FUN()来替换。
1 if(判断条件)
2 REPLACE_FUN();
但是实际上在预处理的时候,宏展开替换后变成了:
1 if(判断条件)
2 funA();
3 funB(); //此处funB()一定会执行,造成逻辑错误
可以看出,funB()不会按照判断条件才去执行。而是变成了一条独立的语句,而如果在宏中使用括号:
1 #define REPLACE_FUN() {funA(); funB();}
我们一般的代码习惯都会在语句的末尾加上分号,因此也会出错:
1 if(判断条件)
2 REPLACE_FUN();
3 //宏展开后为:
4 if(判断条件)
5 {
6 funA();
7 funB();
8 }; //此处替换后多一个分号;导致编译报错
因此,针对这种多条重复语句的连续使用,如果想用宏替换实现这个作用域的功能,就可以考虑使用do {...} while(0)语句:
1 define REPLACE_FUN() \
2 do{ \
3 funA();\
4 funB();\
5 }while(0)\
6 //宏展开前为:
7 if(判断条件)
8 REPLACE_FUN();
9 //宏展开后为:
10 if(判断条件)
11 do{
12 funA();
13 funB();
14 }while(0); //根据判断条件,正确执行了一次逻辑

2、避免goto语句的使用
goto语句也称为无条件转移语句,使用后可以从多重循环或者多个判断中直接跳出。对于如下例子:
1 void fun(int a)
2 {
3 if(1 == a)
4 {
5 ...//todo
6 goto exit;
7 }
8 if(2 == a)
9 {
10 ...//todo
11 goto exit;
12 }
13 exit:
14 ...//todo
15 printf("a is error"\n);
16 }
但是为了程序结构的清晰,还是要尽量限制goto语句的使用,我们可以使用do {...} while(0)结构配合break跳出单层的循环的方法来替代这种goto的用法。
1 int fun(int a)
2 {
3 do{
4 if(1 == a)
5 {
6 ...//todo
7 break;
8 }
9 if(2 == a)
10 {
11 ...//todo
12 break;
13 }
14 }while(0);
15 ...//todo
16 printf("a is error"\n);
17 }
3、定义一个单独的函数块来实现复杂的操作
当某个函数程序功能较为复杂,在该函数的代码段中如果不再单独定义一个函数实现部分逻辑,可以使用do {...} while(0)作为一个代码块,将想要实现的逻辑放在do {...} while(0)中,同时在该在do {...} while(0)代码块中定义的变量,可以不用考虑和函数之前或者之后的变量名重复冲突的问题。但是为了代码的易读性,还是尽量声明不同的变量名。
1 int a;
2 char b;
3 int func()
4 {
5 int a = 3;
6 char b = 5;
7 do{
8 int a;
9 char b;
10 ......//todo
11 }while(0);
12 }

4、避免空宏的警告
有的时候,程序为了不同的平台移植或者不同架构的限制,很多时候会先定义空宏,后续再根据实际的需要看是否定义具体内容。但是在编译的时候,这些空宏可能会给出warning,为了避免这样的warning,我们可以使用do{...}while(0)来定义空宏,这种情况不太常见,因为有很多编译器已经支持空宏。
1 //空宏
2 #define EMPTY_FUN
3 //增加do{...}while(0)来定义空宏
4 #define EMPTY_FUN do{}while(0) //避免了可能的编译warning
更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”

你想知道的do{...}while(0)的作用,都在这里了的更多相关文章
- 你想知道的3D Touch开发全在这里了
前言 iPhone 6s和iPhone 6s Plus为多点触摸界面带来了强大的3D触摸新维度.这项新技术可以感知用户按下显示屏的深度,让他们比以往任何时候都更能使用你的应用程序和游戏.更多关于3D ...
- [翻译]你不会想知道的kobject,kset,和ktypes
---------------------------------------------------------------------------------------------------- ...
- 你想知道的 std::vector::push_back 和 std::vector::emplace_back
引言 C++ 11 后,标准库容器 std::vector 包含了成员函数 emplace 和 emplace_back.emplace 在容器指定位置插入元素,emplace_back 在容器末尾添 ...
- CLR中你想知道的事
CLR是什么? CLR 公共语言运行时,是一个可由多个语言共同使用的运行环境,核心(内存管理,程序集加载,安全性,异常处理和多线程) Visual Studio是一种编译器,编译器也可称为语法检查器和 ...
- All in One 你想知道的 hacker 技术都在这里
作者:HelloGitHub-小鱼干 hacker 这个词,大多数理解为黑客,而维基百科对其的定义为--黑客(Hacker)是指对设计.編程和计算机科学方面具高度理解的人,在本文中 hacker 主要 ...
- 关于Jmeter3.0,你必须要知道的5点变化
2016.5.18日,Apache 发布了jmeter 3.0版本,本人第一时间上去查看并下载使用了,然后群里或同事都会问有什么样变化呢?正好在网上看到一遍关于3.0的文章,但是是英文的.这里翻译一下 ...
- 【转】关于Jmeter3.0,你必须要知道的5点变化
2016.5.18日,Apache 发布了jmeter 3.0版本,本人第一时间上去查看并下载使用了,然后群里或同事都会问有什么样变化呢?正好在网上看到一遍关于3.0的文章,但是是英文的.这里翻译一下 ...
- (0)开始 Raspberry Pi 项目前需要知道的 10 件事
https://www.digikey.cn/zh/articles/techzone/2017/feb/10-things-to-know-before-starting-a-raspberry-p ...
- Git / 程序员需要知道的12个Git高级命令
众所周知,Git目前已经是分布式版本控制领域的翘楚,围绕着Git形成了完整的生态圈.学习Git,首先当然是学习Git的基本工作流.相比于SVN等传统版本控制系统来说,Git是专为分布式版本控制而生的强 ...
- 关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造)
关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造) 摘要:在中文搜索中的标点.符号往往也是有语义的,比如我们要搜索“C++”或是“C#”,我们不希望搜索出来的全是“C”吧?那样对程 ...
随机推荐
- xamarin.Android自动升级
在写这边文章的时候参考了很多其他人的博客,在此感谢各位.也是给其他的伙伴做一个总结.对于我们新手来说存在的问题还是比较多的. 一.搭建iis服务器 首先我们需要搭建一个可以提供app下载最新版本的网站 ...
- 教你用Python制作BMI计算器
案例介绍 欢迎来到我的小院,我是霍大侠,恭喜你今天又要进步一点点了!我们来用Python相关知识,做一个BMI计算器的案例.你可以通过控制台的提示信息,输入身高和体重,注意单位,系统会自动计算出BMI ...
- 使用python脚本传递参数:(三种方式可收藏)
背景:使用python脚本传递参数在实际工作过程中还是比较常用,以下提供了好几种的实现方式: 一.使用sys.argv的数组传入说明:使用sys.argv必须按照先后的顺序传入对应的参数:sys.ar ...
- .NET 6 实现滑动验证码(四)、扩展类
为了能够通过配置文件(appsettings.json)或通过代码进行背景图片与模板进行配置.可自定义资源类型.自定义验证规则,本节创建一些扩展类,用来实现这些功能. 上一节内容:NET 6 实现滑动 ...
- 【JVM故障问题排查心得】「内存诊断系列」Docker容器经常被kill掉,k8s中该节点的pod也被驱赶,怎么分析?
背景介绍 最近的docker容器经常被kill掉,k8s中该节点的pod也被驱赶. 我有一个在主机中运行的Docker容器(也有在同一主机中运行的其他容器).该Docker容器中的应用程序将会计算数据 ...
- vue elementui弹框内 富文本编辑器的使用,及踩坑
最近vue项目中遇到弹框内使用富文本编辑器,遇到最大的问题是,在打开弹框后才能创建富文本编辑器,并且只能创建一次,多次点击弹框,报错: Error in v-on handler: "Err ...
- [R语言] 基于R语言实现环状条形图的绘制
环状条形图(Circular barplot)是条形图的变体,图如其名,环状条形图在视觉上很吸引人,但也必须小心使用,因为环状条形图使用的是极坐标系而不是笛卡尔坐标系,每一个类别不共享相同的Y轴.环状 ...
- Java基础篇——常用类
Objec类 Test t1 = new Test(); Test t2 = new Test(); //toString方法,对某个对象的字符串输出 //getClass方法,返回该对象的Class ...
- P8881 懂事时理解原神
简要题意 \(T\) 组数据,每组数据给出一个 \(n\) 个顶点,\(m\) 条边的无向无权图.求出使用下面的伪代码求 \(1\) 为源点的单源最短路答案正确的概率.保留 \(3\) 位小数. in ...
- Matplotlib 绘制折线图
Matplotlib matplotlib: 最流行的Python底层绘图库,主要做数据可视化图表,名字取材于MATLAB,模仿MATLAB构建 绘制折线图 绘制两小时的温度变化 from matpl ...