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)的作用,都在这里了的更多相关文章

  1. 你想知道的3D Touch开发全在这里了

    前言 iPhone 6s和iPhone 6s Plus为多点触摸界面带来了强大的3D触摸新维度.这项新技术可以感知用户按下显示屏的深度,让他们比以往任何时候都更能使用你的应用程序和游戏.更多关于3D ...

  2. [翻译]你不会想知道的kobject,kset,和ktypes

    ---------------------------------------------------------------------------------------------------- ...

  3. 你想知道的 std::vector::push_back 和 std::vector::emplace_back

    引言 C++ 11 后,标准库容器 std::vector 包含了成员函数 emplace 和 emplace_back.emplace 在容器指定位置插入元素,emplace_back 在容器末尾添 ...

  4. CLR中你想知道的事

    CLR是什么? CLR 公共语言运行时,是一个可由多个语言共同使用的运行环境,核心(内存管理,程序集加载,安全性,异常处理和多线程) Visual Studio是一种编译器,编译器也可称为语法检查器和 ...

  5. All in One 你想知道的 hacker 技术都在这里

    作者:HelloGitHub-小鱼干 hacker 这个词,大多数理解为黑客,而维基百科对其的定义为--黑客(Hacker)是指对设计.編程和计算机科学方面具高度理解的人,在本文中 hacker 主要 ...

  6. 关于Jmeter3.0,你必须要知道的5点变化

    2016.5.18日,Apache 发布了jmeter 3.0版本,本人第一时间上去查看并下载使用了,然后群里或同事都会问有什么样变化呢?正好在网上看到一遍关于3.0的文章,但是是英文的.这里翻译一下 ...

  7. 【转】关于Jmeter3.0,你必须要知道的5点变化

    2016.5.18日,Apache 发布了jmeter 3.0版本,本人第一时间上去查看并下载使用了,然后群里或同事都会问有什么样变化呢?正好在网上看到一遍关于3.0的文章,但是是英文的.这里翻译一下 ...

  8. (0)开始 Raspberry Pi 项目前需要知道的 10 件事

    https://www.digikey.cn/zh/articles/techzone/2017/feb/10-things-to-know-before-starting-a-raspberry-p ...

  9. Git / 程序员需要知道的12个Git高级命令

    众所周知,Git目前已经是分布式版本控制领域的翘楚,围绕着Git形成了完整的生态圈.学习Git,首先当然是学习Git的基本工作流.相比于SVN等传统版本控制系统来说,Git是专为分布式版本控制而生的强 ...

  10. 关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造)

    关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造) 摘要:在中文搜索中的标点.符号往往也是有语义的,比如我们要搜索“C++”或是“C#”,我们不希望搜索出来的全是“C”吧?那样对程 ...

随机推荐

  1. WebApi实现Token验证

    为什么要实现Token呢.在我们客户端发送请求时,如果没有校验数据是否合法那么就有可能造成非法请求泄露我们的数据 实现Token的思路 1.客户端通过用户名和密码来获取Token 通过自己的账号和密码 ...

  2. 在C#中使用Halcon开发视觉检测程序

    目录 简介 将 HALCON/.NET 添加到应用程序 添加控件 引用dll 调用Halcon算子 程序示例 HSmartWindowControl控件使用 加载.保存图像 扩展:加载相机图像 画线. ...

  3. Python异步协程(asyncio详解)

    续上篇讲解yield from博客,上篇链接:https://www.cnblogs.com/Red-Sun/p/16889182.html PS:本博客是个人笔记分享,不需要扫码加群或必须关注什么的 ...

  4. 【SQL进阶】【CASE/IF、COUNT/SUM、多条记录拼接为一个内容】Day03:聚合分组查询

    〇.今日内容概述 一.聚合函数 1.SQL类别高难度试卷得分的截断平均值[去最高最低分求平均] 自己的想法 SELECT tag, difficulty, ROUND((SUM(score)-MIN( ...

  5. if-else 的优美写法

    前言‍♂️ 相信大家或多或少都接触过拥有庞大 if else 的项目代码吧,多重嵌套的 if else 在维护的时候真的让人很恼火,有时候一个 bug 排查下来,严重感觉身体被掏空. 本文并未有消灭或 ...

  6. Jmeter 之 switch 控制器

    switch 控制器作用: switch 控制器起到了分流作用,具体应用在并发时,一部分用户执行某个场景,一部分用户执行另外一种场景,就像吞吐量控制器进行分流操作 switch 控制器字段介绍: 实例 ...

  7. snprintf拼接字符串

    例如编辑一个txt文档,不断将字符输入,最终形成一个长句子.可以看成是字符串的不断拼接.snprintf函数具有这个功能. #include<stdio.h> void main(void ...

  8. 第一章 --------------------WPF基础概述

    1.在使用WPF之前我一直在思考为什么要使用WPF? 主要原因在于我已经受够了MFC和Winform 和QT的界面设计.尤其是MFC的界面设计,使用一个界面库十分的复杂,并且我的绝大多数时间都是用在这 ...

  9. [python]《Python编程快速上手:让繁琐工作自动化》学习笔记7

    1. 用GUI 自动化控制键盘和鼠标第18章 (代码下载) pyautogui模块可以向Windows.OS X 和Linux 发送虚拟按键和鼠标点击.根据使用的操作系统,在安装pyautogui之前 ...

  10. [常用工具] OpenCV_contrib库在windows下编译使用指南

    本文主要讲述opencv及opencv_contrib库在windows下基于vs2017编译安装指南.所用OpenCV版本为OpenCV4.4,编译平台为vs2017. 文章目录 1 下载 2 编译 ...