0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C程序中do{}while(0)宏定义的作用

小麦大叔 来源:技术让梦想更伟大 作者:李肖遥 2022-05-23 12:44 次阅读

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

do{conditional code}while(condition)结构

流程图如下:a81b35c4-d515-11ec-bce3-dac502259ad0.png一般结构如以下代码

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=newCCImage();
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. 为了宏展开的时候不会出错。如果直接放在花括号里会出错的

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

#defineDOSOMETHING()action1();action2();

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

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

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

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

这展开存在两个问题:

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

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

#defineDOSOMETHING(){action1();action2();}

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

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

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

那么办法来了

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

#defineDOSOMETHING()
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;
//dosomethinghere
if(!b)break;
//doanotherthinghere
}while(0);
  1. 变形的goto,有些公司不让用goto。在一些函数中,需要实现条件转移,或者构成循环,跳出循环体,使用goto总是一种简单的方法,例如:
#include
#include
intmain()
{
char*str;

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

printf("helloworld
");

loop:
printf("mallocsuccess
");

return(0);
}

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

#include
#include
intmain()
{
do{
char*str;

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

printf("helloworld
");
}while(0);

printf("mallocsuccess
");

return(0);
}

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

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

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

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

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

intkey;
stringvalue;
intfunc()
{
intkey=GetKey();
stringvalue=GetValue();
dosomethingforkey,value;
do{
intkey;stringvalue;
dosomethingforthiskey,value;
}while(0);
}

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

原文标题:do{} while(0) 只执行一次无意义?你可能真的没理解

文章出处:【微信公众号:小麦大叔】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C程序
    +关注

    关注

    4

    文章

    253

    浏览量

    35750
  • 宏定义
    +关注

    关注

    0

    文章

    48

    浏览量

    8927

原文标题:do{} while(0) 只执行一次无意义?你可能真的没理解

文章出处:【微信号:knifewheat,微信公众号:小麦大叔】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C语言中whiledo-while循环的用法

    循环是一种重要的控制结构,可以使程序重复执行一段代码,直到满足特定条件为止。在C语言中,whiledo-while是两种常用的循环结构,本文将详细介绍这两种循环的用法。
    发表于 08-18 16:35 1149次阅读
    C语言中<b class='flag-5'>while</b>和<b class='flag-5'>do-while</b>循环的用法

    定义下的“GPIO_”是什么作用,一定要用吗

    #defineGPIO_KEY P1//独立键盘用P1口#defineGPIO_LED P0//led使用P0口我想知道这个定义下的“GPIO_”是什么
    发表于 05-29 12:22

    C语言定义#和##的作用

    #和##的作用是:前者将定义的变量转化为字符串;后者将其前后的两个定义的两个变量无缝拼接在
    发表于 08-23 18:22

    C语言中的

    ; \}while(0)这种结构在函数里非常常见,它不仅可以在调用后加分号保持代码的格式一致性,还可以避免一些复杂的定义产生的错误。当然
    发表于 12-13 15:32

    delaydo while语句求解?

    ) SysTick->VAL =0x00;//清空计数器 SysTick->CTRL=0x01 ; //开始倒数 do {temp=SysTick->CTRL; } while
    发表于 06-12 06:58

    【原创文章】程序的调试和使用的技巧

    ,即使程序写很多DEBUG_OUT,编译器也会将其处理为没有任何语句。注意:一条语句太长换行需要在每行的结尾使用\,表示下一行的内容是和上面的连续的。使用do...while
    发表于 08-18 17:27

    如何使用C51运行库定义

    Read_SP37_ID(unsigned char idata *IDH,unsigned char idata *IDL){*IDH = CBYTE[0x57E0];*IDL = CBYTE[0x57E1];}一.使用C5
    发表于 12-02 07:55

    C语言定义使用do{}while(0)的好处是什么

    C语言定义使用do{}while(0)的好处1. 概述经常写项目代码,有时需要用到
    发表于 02-25 06:28

    Proteus之do-while语句的应用

    Proteus之do-while语句的应用,很好的Proteus资料,快来学习吧。
    发表于 04-18 14:49 0次下载

    HAL库中do{...} while(0U) 宏定义作用和意义

    HAL库中do{...} while(0U)宏定义作用和意义
    的头像 发表于 03-03 14:07 3032次阅读

    do{...} while(0U) 这种代码的作用和意义

    库的朋友都知道,源码里面有许多do{...} while(0U)的宏定义语句。   今天讲的主要知识点是:HAL库中do{...} while
    的头像 发表于 06-04 14:00 2227次阅读
    <b class='flag-5'>do</b>{...} <b class='flag-5'>while</b>(0U) 这种代码的<b class='flag-5'>作用</b>和意义

    C语言基础:宏定义使用do{}while(0)的好处

    C语言宏定义使用do{}while(0)的好处1. 概述 经常写项目代码,有时需要用到宏定义,而宏定义的用法是否标准,则是会影响到是否能快速
    发表于 01-13 13:06 2次下载
    C语言基础:宏<b class='flag-5'>定义</b>使用<b class='flag-5'>do</b>{}<b class='flag-5'>while</b>(0)的好处

    如何去区分whiledo while 的用法

    如何去区分whiledo while 的用法。 2 方法 Do while
    的头像 发表于 02-24 10:40 740次阅读

    C语言-宏定义中使用do{...} while(0)到底图个啥

    在 Linux 内核中,经常会看到do{} while(0)这样的语句,许多人开始都会疑惑,认为do{} while(0)毫无意义,因为它只会执行一次,加不加
    的头像 发表于 06-11 10:59 485次阅读

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

    在嵌入式开发中,宏定义非常强大也非常便捷,如果正确使用可以让你的工作事半功倍。然而,在很多的C程序中,你可能会看到不是那么直接的比较特殊一点的宏定义,比如do{}
    的头像 发表于 10-09 14:20 268次阅读
    <b class='flag-5'>do</b>{}<b class='flag-5'>while</b>(0)只执行一次有意义吗?