你想知道的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();}

        我们一般的代码习惯都会在语句的末尾加上分号,因此也会出错:

你想知道的do{...}while(0)的作用,都在这里了

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);    //根据判断条件,正确执行了一次逻辑

你想知道的do{...}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 }

你想知道的do{...}while(0)的作用,都在这里了


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)的作用,都在这里了

原文链接:https://www.cnblogs.com/Sharemaker/p/17142670.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:你想知道的do{…}while(0)的作用,都在这里了 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • Go语言中的数据格式(json、xml 、msgpack、protobuf)使用总结

    下面我就来详细讲解一下“Go语言中的数据格式(json、xml、msgpack、protobuf)使用总结”。 1. 前言 在Web开发和大数据场景中,数据格式的选择对性能和可读性都有着很大的影响,因此我们需要对不同的数据格式进行适当的选择。Go语言中,常用的数据格式主要有json、xml、msgpack和protobuf四种,下面就每一种格式分别进行总结。…

    C 2023年5月23日
    00
  • C语言实现图书管理系统课程设计

    C语言实现图书管理系统课程设计攻略 需求分析 需要建立一个系统,管理图书的各种信息,包括图书的基本信息、借阅记录、归还记录等,能够方便快捷地进行增删改查的操作。 图书管理员需要有添加、删除、修改、查询图书、查询借阅记录等权限。 读者需要有借书、还书和查看自己借阅记录的权限。 系统需要支持多用户接入,管理员和读者应该有不同的登录界面。 技术选型 编程语言:C语…

    C 2023年5月23日
    00
  • C语言实现贪吃蛇超详细教程

    C语言实现贪吃蛇超详细教程 1. 简介 贪吃蛇是一款非常经典的游戏,同时其也是初学者学习编程的一个很好的练习项目,本教程将带领大家使用C语言来实现贪吃蛇。 2. 实现步骤 2.1 初始化 首先,我们需要初始化游戏窗口、贪吃蛇的位置、食物的位置以及其他一些必要的变量。 以Windows窗口为例,我们可以使用WinAPI来创建一个窗口,并使用CreateWind…

    C 2023年5月22日
    00
  • C++中的extern “C”用法详解

    C++中的extern “C”用法详解 简介 在C++中,存在着C和C++的二进制兼容性问题,即C++编译后的函数名与C编译后的函数名不一样。这会导致当我们在头文件中声明一个C++函数的时候,在C语言中无法使用这个函数。所以我们需要在C++ 中使用 extern “C” 关键字声明特定函数,以便在 C++ 环境下使用 C 标准程序声明及定义的函数。 用法 使…

    C 2023年5月23日
    00
  • C语言函数超详细讲解下篇

    我来为您详细讲解一下“C语言函数超详细讲解下篇”的完整攻略。 一、前言 本文将会重点介绍 C 语言中函数的相关知识,主要包括以下几个部分: 函数的概念及基本使用方法。 函数的参数传递方式及注意事项。 函数的返回值类型及返回值的使用方法。 递归函数的使用方法及注意事项。 二、函数的概念及基本使用方法 函数是 C 语言中的一种重要的代码模块化机制,它通常由一段可…

    C 2023年5月23日
    00
  • C++设计模式之简单工厂模式实例

    C++设计模式之简单工厂模式实例详解 简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式。简单工厂模式定义了一个工厂类,它可以根据所传递的参数或配置文件的不同,返回不同类的实例。简单工厂模式具有简单易懂,适用范围广等特点,在实际开发中也得到了广泛应用。 简单工厂模式的结构 简单工厂模式包含三个主要…

    C 2023年5月22日
    00
  • C语言中栈和队列实现表达式求值的实例

    C语言中栈和队列实现表达式求值的实例 在 C 语言中,可以利用栈和队列来实现表达式求值。表达式求值是将字符串形式的表达式转换成计算结果的过程,包括算数表达式和逻辑表达式两种类型。下面将分别对这两种表达式求值进行实例说明。 算数表达式求值 算数表达式求值的过程包括词法分析、语法分析和计算三个过程。词法分析是将字符串表达式拆分成由数字、运算符和括号等组成的多个 …

    C 2023年5月23日
    00
  • C语言中递归和排列组合详解

    C语言中递归和排列组合详解 递归 递归是指一个函数在其定义或调用中又直接或间接地调用了自身的一种方式。在 C 语言中,递归是一种简单而强大的编程技术。递归通常用于解决问题比较复杂的情况,特别是问题可以被分解成许多解题相似的小问题时。 递归函数 在 C 语言中,递归函数是指在其函数定义中调用了自身的函数。下面是一个递归函数的示例: int factorial(…

    C 2023年5月24日
    00
合作推广
合作推广
分享本页
返回顶部