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

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

3天内不再提示

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

Dp1040 来源:技术让梦想更伟大 2023-10-09 14:20 次阅读

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

do{conditional code}while(condition)结构

流程图如下:

wKgZomUjnDaAO7DeAAAtHupiDQM585.jpg

一般结构如以下代码:

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

do while/while do的区别

do while()

意思是先干了再说!!

while() do

意思是先看看能不能干!

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

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

do{
...
}while(0)

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

总结了7种妙处

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

有时候只是为了代码分块,比仅仅使用{}更直观些。例如在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);

为了宏展开的时候不会出错。如果直接放在花括号里会出错的。

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

#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内核开发)给我们解答如下:

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

当你执行一段代码到一半,想跳过剩下的一半的时候,如果你正处于do{...}while(0)循环中,则能用break达到这个目的。

do
{
执行.
再执行…
if(如果有什么条件满足)
{
我想跳到另外一段代码了,剩下的不执行了,可是不建议用goto语句,怎么办呢?
break;/*搞定*/
}
我有可能被执行.
}while(false)

举个例子如下:

do
{
if(!a)break;
//dosomething here
if(!b)break;
//doanother thing here
}while(0);

变形的goto,有些公司不让用goto。在一些函数中,需要实现条件转移,或者构成循环,跳出循环体,使用goto总是一种简单的方法,例如:

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

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

printf("hello world
");

loop:
printf("malloc success
");

return(0);
}

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

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

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

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

printf("malloc success
");

return(0);
}

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

可以是兼容各种编译器。

int a;
a=10;
int b;
b=20;

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

int a;
a=10;
do
{
int b;
b=20;
}while(0);

避免由宏引起的警告 内核中由于不同架构的限制,很多时候会用到空宏。在编译的时候,这些空宏会给出警告,为了避免这样的warning,我们可以使用do{...}while(0)来定义空宏:

#define DOSOMETHING()do{}while(0)

定义单一的函数块来完成复杂的操作。

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

int key;
string value;
intfunc()
{
int key=GetKey();
string value=GetValue();
dosomethingforkey,value;
do{
int key;string value;
dosomethingforthis key,value;
}while(0);
}

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






审核编辑:刘清

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

    关注

    18

    文章

    1013

    浏览量

    47453
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21597

原文标题:do{}while(0)只执行一次无意义?

文章出处:【微信号:玩点嵌入式,微信公众号:玩点嵌入式】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    高通收购NXP真的有意义吗?

    市场传言,Qualcomm收购NXP Semiconductor的洽谈已经接近成交;但是,这桩交易对Qualcomm来说真的有意义吗?
    发表于 10-27 10:43 3184次阅读

    怎么让while循环执行一次??

    在做堆栈的程序,因为要用到循环的移位寄存器,但是又每次只需要执行一次,怎么解决呢?
    发表于 07-29 11:45

    while循环执行一次

    ); //延时5msif(key==0)flag=1;}}void main(){ anjian(); while(flag==1){**(); //某个函数}}调试发现while里面的函数只
    发表于 11-13 13:15

    怎么让ucosii中的部分代码执行一次执行一次后不在执行

    虽然ucosii是多任务调度的,但是我想让任务中的部分代码执行一次执行一次后不在执行,不知
    发表于 07-01 03:45

    请问为什么程序在while循环中执行一次函数?

    嗨,我很难理解为什么我的程序在while循环中执行一次函数,即使循环是无限的。这是我的计划:
    发表于 10-28 11:26

    Java教程之如何进行Java中的do-while循环

    我们知道当开始循环条件就不满足的时候,while循环一次也不会 执行。有的时候。我们有这样的需要:无论如何循环都先执行
    发表于 01-23 11:05 7次下载
    Java教程之如何进行Java中的<b class='flag-5'>do-while</b>循环

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

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

    安全自动化的5种有意义的方法

    企业如今面临日益严重的网络威胁,安全自动化将为企业IT团队提供帮助。 关于安全性的个棘手问题是,这就像是“打鼹鼠”的游戏。一次性处理可能很简单,因为许多漏洞可以修补,并且企业可能已经制定了使用安全
    的头像 发表于 12-10 15:08 1989次阅读

    苹果造车真的有意义

    本周有三个话题值得我们关注,首先,近两天苹果的造车的消息引起了大家热烈的关注,众多类似“看到装了四个轮子的手机长什么样”的评论也是铺天盖地的传来,所以苹果造车真的有意义吗?其次,丰田掌门人丰田章男
    的头像 发表于 12-28 10:23 1818次阅读

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

    些项目中,我们可能看到过很多,在学习的时候没有见过的代码,比如 do{...} while(0U) 这种代码,在我们学习的时候可能很少见,但实际项目中却存在很多类似代码。 今天就来
    的头像 发表于 06-04 14:00 2650次阅读
    <b class='flag-5'>do</b>{...} <b class='flag-5'>while</b>(<b class='flag-5'>0</b>U) 这种代码的作用和<b class='flag-5'>意义</b>

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

    推荐用do{}while(0)这种用法呢?主要有两点原因,个是代码适应性以及拓展性,第二个是为了尽量避免些不必要的语法和逻辑错误。比如保
    发表于 01-13 13:06 2次下载
    C语言基础:宏定义使用<b class='flag-5'>do</b>{}<b class='flag-5'>while</b>(<b class='flag-5'>0</b>)的好处

    基于Arduino的矫枉过正但具有意义的COVID流行病展示

    电子发烧友网站提供《基于Arduino的矫枉过正但具有意义的COVID流行病展示.zip》资料免费下载
    发表于 11-01 09:38 0次下载
    基于Arduino的矫枉过正但具<b class='flag-5'>有意义</b>的COVID流行病展示

    如何去区分whiledo while 的用法

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

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

    在 Linux 内核中,经常会看到do{} while(0)这样的语句,许多人开始都会疑惑,认为do{} while(
    的头像 发表于 06-11 10:59 770次阅读

    WHILE语句如何执行

    。 语法(Syntax): WHILE语句根据下列规则执行: 在循环体的重复之前,求值执行条件。 执行条件值为TRUE(真)时,跟在DO后的
    的头像 发表于 09-10 09:48 782次阅读
    <b class='flag-5'>WHILE</b>语句如何<b class='flag-5'>执行</b>