do{}while(0)只执行一次无意义?

嵌入式ARM 2022-06-08 12:01


在嵌入式开发中,宏定义非常强大也非常便捷,如果正确使用可以让你的工作事半功倍。然而,在很多的C程序中,你可能会看到不是那么直接的比较特殊一点的宏定义,比如do{}while(0)

do{conditional code}while(condition)结构

流程图如下:


一般结构如以下代码:

do
{
  //循环体
}
while (条件表达式);

do while/while do的区别

  • do while()

意思是先干了再说!!

  • while() do

意思是先看看能不能干!

初见do{...}while(0)

linux内核和其他一些开源的代码中,经常会遇到这样的代码:

do{
 ...
}while(0)

这样的代码一看就不是一个循环,do..while表面上在这里一点意义都没有,只执行一次而已,那么为什么要这么用呢?

总结了7种妙处

实际上,do{...}while(0)的作用可不止一点点,下面我列举了一些。

  1. 有时候只是为了代码分块,比仅仅使用{}更直观些。例如在cocos2d-x代码中。
do
{
    CCImage* pImage = new CCImage();
    CC_BREAK_IF(NULL == pImage);
    bRet = pImage->initWithString(text, (int)dimensions.width, (int)dimensions.height, eAlign, fontName, (int)fontSize);
    CC_BREAK_IF(!bRet);
    bRet = initWithImage(pImage);
    CC_SAFE_RELEASE(pImage);
while (0);
  1. 为了宏展开的时候不会出错。如果直接放在花括号里会出错的。

举例来说,假设你需要定义这样一个宏:

#define DOSOMETHING() action1(); action2();

这个宏的本意是,当执行DOSOMETHING()时,action1(),action2()都会被调用。如果有判断,再执行这个宏的话,如下:

if(NULL == pPointer)
  DOSOMETHING();
else
  ...

这样宏在预处理的时候会直接被展开,放在花括号里,那么实际上写的代码如下:

if(NULL == pPointer)
    action1();
action2();
else
  ...

这展开存在两个问题:

  • 因为if分支后面有两个语句,导致else分支没有对应的if,编译失败。
  • 假设没有else分支,则DOSOMETHING中的第二个语句无论if测试是否通过,都会执行。

那么,仅仅使用{}把action1()、action2()包起来行么?比如:

#define DOSOMETHING() { action1(); action2(); }

我们在写代码的时候都习惯在语句右面加上分号,如果在宏中使用{},代码编译展开后宏就相当于这样写了:{...};,展开后如下:

if(NULL == pPointer)
{
    action1();
    action2();
};
else
  ...

这段代码中大括号后多了一个分号,如果有else,那么else又没有对应的if了,编译出错。

那么办法来了!

如果我们使用do{...}while(0)来定义宏,即:

#define DOSOMETHING() \
        do{ \
          action1();\
          action2();\
        }while(0)\

宏被展开后,上面的调用语句会保留初始的语义,同时绝大部分编译器都能够识别do{...}while(0)这种无用的循环并进行优化,不会导致性能优化的降低。

小结:

在Linux内核和驱动代码还有cocos2d-x中,很多宏实现都使用do{...}while(0)来包裹他们的逻辑,Google的Robert Love先前从事Linux内核开发)给我们解答如下:

让你定义的宏总是以相同的方式工作,不管在调用代码中怎么使用分号和大括号,而该宏总能确保其行为是一致的。

  1. 当你执行一段代码到一半,想跳过剩下的一半的时候,如果你正处于do{...}while(0)循环中,则能用break达到这个目的。
do
{
  执行.
  再执行…
  if (如果有什么条件满足)
  {
    我想跳到另外一段代码了,剩下的不执行了,可是不建议用goto语句,怎么办呢?
     break;/*搞定*/
  }
  我有可能被执行.
}while(false)

举个例子如下:

do
{
  if(!a) break;
  //do something here
  if(!b) break;
  //do another thing here   
}while(0);
  1. 变形的goto,有些公司不让用goto。在一些函数中,需要实现条件转移,或者构成循环,跳出循环体,使用goto总是一种简单的方法,例如:
#include 
#include 
int main()
{
   char *str;
 
   /* 最初的内存分配 */
   str = (char *) malloc(15);
   if(str != NULL)
     goto loop;
 
   printf("hello world\n");
 
loop:
   printf("malloc success\n");

   return(0);
}

但是,由于goto不符合软件工程的结构化,而且有可能使得代码难懂,所以很多人都不倡导使用,这个时候我们可以使用do{...}while(0)来做同样的事情:

#include 
#include 
int main()
{
  do{
      char *str;

      /* 最初的内存分配 */
      str = (char *) malloc(15);
      if(str != NULL)
       break;

      printf("hello world\n");
  }while(0);

   printf("malloc success\n");

   return(0);
}

这里将函数主体部分使用do{...}while(0)包含起来,使用break来代替goto,后续的清理工作在while之后,现在既能达到同样的效果,而且代码的可读性、可维护性都要比上面的goto代码好的多了。

  1. 可以是兼容各种编译器。
int a;
a = 10;
int b;
b = 20;

这种代码在只支持c89的编译器上是编译不过去的,比如ADS 2.0。

int a;
a = 10;
do
{
   int b;
   b = 20;
}while(0);
  1. 避免由宏引起的警告 内核中由于不同架构的限制,很多时候会用到空宏。在编译的时候,这些空宏会给出警告,为了避免这样的warning,我们可以使用do{...}while(0)来定义空宏:
#define DOSOMETHING() do{}while(0)
  1. 定义单一的函数块来完成复杂的操作。

如果你有一个复杂的函数,变量很多,而且你不想要增加新的函数,可以使用do{...}while(0),将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复,例如:

int key;
string value;
int func()
{
    int key = GetKey();
    string value = GetValue();
    dosomething for key,value;
    do{
        int key;string value;
        dosomething for this key,value;
    }while(0);    
}

但为了代码的可读性,尽量声明不同的变量名,以便于后续开发人员欣赏。

END

作者:李肖遥
来源:技术让梦想更伟大

版权归原作者所有,如有侵权,请联系删除。

推荐阅读
嵌入式硬件转软件,请慎重!
菜鸟教程:手把手教你写一个安卓APP
成为嵌入式高手,少不了这100多个软硬件开源项目!

→点关注,不迷路←
嵌入式ARM 关注这个时代最火的嵌入式ARM,你想知道的都在这里。
评论
  •         温度传感器的精度受哪些因素影响,要先看所用的温度传感器输出哪种信号,不同信号输出的温度传感器影响精度的因素也不同。        现在常用的温度传感器输出信号有以下几种:电阻信号、电流信号、电压信号、数字信号等。以输出电阻信号的温度传感器为例,还细分为正温度系数温度传感器和负温度系数温度传感器,常用的铂电阻PT100/1000温度传感器就是正温度系数,就是说随着温度的升高,输出的电阻值会增大。对于输出
    锦正茂科技 2024-12-03 11:50 111浏览
  • RDDI-DAP错误通常与调试接口相关,特别是在使用CMSIS-DAP协议进行嵌入式系统开发时。以下是一些可能的原因和解决方法: 1. 硬件连接问题:     检查调试器(如ST-Link)与目标板之间的连接是否牢固。     确保所有必要的引脚都已正确连接,没有松动或短路。 2. 电源问题:     确保目标板和调试器都有足够的电源供应。     检查电源电压是否符合目标板的规格要求。 3. 固件问题: &n
    丙丁先生 2024-12-01 17:37 102浏览
  • 遇到部分串口工具不支持1500000波特率,这时候就需要进行修改,本文以触觉智能RK3562开发板修改系统波特率为115200为例,介绍瑞芯微方案主板Linux修改系统串口波特率教程。温馨提示:瑞芯微方案主板/开发板串口波特率只支持115200或1500000。修改Loader打印波特率查看对应芯片的MINIALL.ini确定要修改的bin文件#查看对应芯片的MINIALL.ini cat rkbin/RKBOOT/RK3562MINIALL.ini修改uart baudrate参数修改以下目
    Industio_触觉智能 2024-12-03 11:28 87浏览
  • 当前,智能汽车产业迎来重大变局,随着人工智能、5G、大数据等新一代信息技术的迅猛发展,智能网联汽车正呈现强劲发展势头。11月26日,在2024紫光展锐全球合作伙伴大会汽车电子生态论坛上,紫光展锐与上汽海外出行联合发布搭载紫光展锐A7870的上汽海外MG量产车型,并发布A7710系列UWB数字钥匙解决方案平台,可应用于数字钥匙、活体检测、脚踢雷达、自动泊车等多种智能汽车场景。 联合发布量产车型,推动汽车智能化出海紫光展锐与上汽海外出行达成战略合作,联合发布搭载紫光展锐A7870的量产车型
    紫光展锐 2024-12-03 11:38 101浏览
  • 戴上XR眼镜去“追龙”是种什么体验?2024年11月30日,由上海自然博物馆(上海科技馆分馆)与三湘印象联合出品、三湘印象旗下观印象艺术发展有限公司(下简称“观印象”)承制的《又见恐龙》XR嘉年华在上海自然博物馆重磅开幕。该体验项目将于12月1日正式对公众开放,持续至2025年3月30日。双向奔赴,恐龙IP撞上元宇宙不久前,上海市经济和信息化委员会等部门联合印发了《上海市超高清视听产业发展行动方案》,特别提到“支持博物馆、主题乐园等场所推动超高清视听技术应用,丰富线下文旅消费体验”。作为上海自然
    电子与消费 2024-11-30 22:03 98浏览
  • 作为优秀工程师的你,已身经百战、阅板无数!请先醒醒,新的项目来了,这是一个既要、又要、还要的产品需求,ARM核心板中一个处理器怎么能实现这么丰富的外围接口?踌躇之际,你偶阅此文。于是,“潘多拉”的魔盒打开了!没错,USB资源就是你打开新世界得钥匙,它能做哪些扩展呢?1.1  USB扩网口通用ARM处理器大多带两路网口,如果项目中有多路网路接口的需求,一般会选择在主板外部加交换机/路由器。当然,出于成本考虑,也可以将Switch芯片集成到ARM核心板或底板上,如KSZ9897、
    万象奥科 2024-12-03 10:24 68浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2024-12-02 10:40 120浏览
  • 最近几年,新能源汽车愈发受到消费者的青睐,其销量也是一路走高。据中汽协公布的数据显示,2024年10月,新能源汽车产销分别完成146.3万辆和143万辆,同比分别增长48%和49.6%。而结合各家新能源车企所公布的销量数据来看,比亚迪再度夺得了销冠宝座,其10月新能源汽车销量达到了502657辆,同比增长66.53%。众所周知,比亚迪是新能源汽车领域的重要参与者,其一举一动向来为外界所关注。日前,比亚迪汽车旗下品牌方程豹汽车推出了新车方程豹豹8,该款车型一上市就迅速吸引了消费者的目光,成为SUV
    刘旷 2024-12-02 09:32 119浏览
  • 11-29学习笔记11-29学习笔记习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-02 23:58 73浏览
  • 概述 说明(三)探讨的是比较器一般带有滞回(Hysteresis)功能,为了解决输入信号转换速率不够的问题。前文还提到,即便使能滞回(Hysteresis)功能,还是无法解决SiPM读出测试系统需要解决的问题。本文在说明(三)的基础上,继续探讨为SiPM读出测试系统寻求合适的模拟脉冲检出方案。前四代SiPM使用的高速比较器指标缺陷 由于前端模拟信号属于典型的指数脉冲,所以下降沿转换速率(Slew Rate)过慢,导致比较器检出出现不必要的问题。尽管比较器可以使能滞回(Hysteresis)模块功
    coyoo 2024-12-03 12:20 111浏览
  • TOF多区传感器: ND06   ND06是一款微型多区高集成度ToF测距传感器,其支持24个区域(6 x 4)同步测距,测距范围远达5m,具有测距范围广、精度高、测距稳定等特点。适用于投影仪的无感自动对焦和梯形校正、AIoT、手势识别、智能面板和智能灯具等多种场景。                 如果用ND06进行手势识别,只需要经过三个步骤: 第一步&
    esad0 2024-12-04 11:20 58浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦