do{...}while(0)的用法,超详解

2023-05-16

转载原文地址:http://blog.csdn.net/majianfei1023/article/details/45246865

零.导引
第一次见到 do{...}while(0)是在学习libevent的时候,看到里面有很多类似

#define TT_URI(want) do { \

char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \

tt_want(ret != NULL); \

tt_want(ret == url_tmp); \

if (strcmp(ret,want) != 0) \

TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \

} while(0)


当时特别疑惑,do{...}while()不是做循环的吗,类似for,while的语法,不过现实开发中,用for和while的比较多,do{...}while()比较少了,算是比较不常用的语法。
但是在这里,这样的代码一看就不是一个循环,do..while表面上在这里一点意义都没有,那么为什么要这么用呢?特别疑惑的google之,恍然大悟,原来do{...}while()还有此等妙用,看来自己还差得远啊。


总体来说,do{...}while(0)有两种用法。

 

一.定义宏,实现局部作用域。

1.大家做c语言题目的时候,一道必考题就是 #define的算术运算。
比如,我随手写一个最简单的#define

#define FUNC(x) x*3+4

...

int result = 2 * FUNC(3);


result输出多少?  26?错!
这是c语言新手一定会犯的错误,至少我上大学的时候第一次看到这,我就做错了。
要知道这道题答案是多少,首先就要知道#define的作用。
1).#define M (a+b) 它的作用是指定标识符M来代替表达式(a+b)。在编写源程序时,所有的(a+b)都可由M代替,而对源程序作编译时,将先由预处理程序进行宏代换,即用(a+b)表达式去置换所有的宏名M,然后再进行编译。
2).c语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。(以上两句来自百度百科)

也就是 #define是在预处理的时候进行直接替换!(这句话是这一节的重点)
例如之上的展开就是.
int result = 2 * x * 3 + 4
x用实参3代替就是:
int result = 2 * 3 * 3 + 4 = 22而不是26.

 

有些人可能说,这些我都知道,这跟do{...}while(0)有什么关系。

其实,我只是为了告诉你,#define使用的时候要特别小心,尤其是#define一个很复杂的逻辑的时候。

我们举个简单的#define的例子:

void print()

{

     cout<<"print: "<<endl;

}


void send()

{

    cout <<"send: "<<endl;

}


#define LOG print();send();


int main(){


if (false)

LOG


cout <<"hello world"<<endl;


system("pause");

return 0;

}


这个代码输出什么?理论上,if(false)里面的代码不会被执行,也就是LOG不会被执行,所以只应该打印出"hello world".

 

但是事实上:

 

纳闷?

注意我上面说的一句话:

也就是 #define是在预处理的时候进行直接替换!(这句话是这一节的重点)

也就是说,上面的if(false)...在这里是:

if (false)

print();

send();


cout <<"hello world"<<endl;


懂了吧。

 

怎么解决了,有些人马上想到,用{...}把#define 的值括住不就可以了。的确,在这里是可以的。

我们在写代码的时候都习惯在语句右面加上分号,如果在宏中使用{},我们通常会这么写:

 

#define LOG {print();send();};

当我们的if后面有一个else呢?

 

就变成了:

if (false)

{

print();

send();

};

else

{

cout <<"hello"<<endl;

}

这样就会因为if语句后面多加了个;而编译不通过。不要说你说,那我不加;那要是你开发一个大型项目的时候你自己也不知道你自己要不要加;了,你就会被自己给绕晕了,所以统一的规范很重要。

 

那么来我们的最终版本:do{...}while(0);

#define LOG do{print();send();}while (0);


int main(){


if (false)

LOG

else

{

cout <<"hello"<<endl;

}



cout <<"hello world"<<endl;


system("pause");

return 0;

}

 

 

就相当于:

if (false)

do{

print();

send();

}while (0);

else

{

cout <<"hello"<<endl;

}



cout <<"hello world"<<endl;


用do{...}while(0);包裹住要操作的#define,无论你外面怎么操作,都不会影响#define的操作。妙哉妙哉啊。

 

 

三.替代goto.

int dosomething()

{

return 0;

}


int clear()

{


}


int foo()

{

int error = dosomething();


if(error = 1)

{

goto END;

}


if(error = 2)

{

goto END;

}


END:

clear();

return 0;

}

当然这只是一个简单的例子,有些人说,我可以不用goto,在每一个goto调用的地方直接,那么加一个判断,你就要加一条clear(),万一你漏了呢?而且正常情况下,foo里面的if有很多个,你要写很多goto,END里面的逻辑也更复杂。这样就更要小心。

 


由于goto不符合软件工程的结构化,而且有可能使得代码难懂,所以很多人都不倡导使用,那这个时候就可以用do{}while(0)来进行统一的管理:

int foo()

{

do

{

int error = dosomething();


if(error = 1)

{

break;

}


if(error = 2)

{

break;

}

} while (0);


clear();

return 0;

}

是不是看起来好看多了,而且还避免了由于错误导致的严重bug(比如你在clear里面是清理内存的操作,你忘记了写goto,而走不到END里面)。

 

在do{...}while(0)里面,在任何地方都可以break跳出,然后继续下面的执行逻辑。即使你不写break,也会在执行完一遍do之后,while(0)不满足,自己跳出去。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

do{...}while(0)的用法,超详解 的相关文章

随机推荐

  • 解析物联网三大实时协议

    解析物联网三大实时协议 2015 08 25 国家物联网标识管理公共服务平台 实时通信技术作为一项根本性前提 xff0c 在物联网应用程序的开发工作中扮演着核心角色 想象一下 xff0c 如果我们能够利用手机与家居环境内的各种小装置进行通信
  • stm32低功耗定时器lptimer在djyos下的应用

    djyos的tickless模式配合低功耗模块可以支持传感器之类功耗要求极端苛刻的应用 xff0c djyos的系统时基如果能用stm32的lptimer来提供 xff0c 堪称妙绝 xff0c APP就可以做到完全不用操心功耗 xff0c
  • 一起学djyos-罗侍田-专题视频课程

    一起学djyos 2195人已学习 课程介绍 djyos是国内原创的开源操作系统 xff0c 采用类BSD许可证 经历13年的时间考验 xff0c 可靠性高 xff0c 实时性高 xff0c 功能强大 xff0c 广泛用于电力系统 自动控制
  • C#中十进制与十六进制之间的转换

    一 十进制转换为十六进制 int a 61 654 string A 61 a ToString 34 X6 34 上面Tostring 34 X6 34 是将整型a转化成16进制数 xff1b 其中 xff1a xff08 1 xff09
  • RS485波形记录与分析

    异步串行数据的一般格式是 xff1a 起始位 43 数据位 43 停止位 xff0c 其中起始位1 位 xff0c 数据位可以是5 6 7 8位 xff0c 停止位可以是1 1 5 2位 对于正逻辑的TTL电平 xff0c a 起始位是一个
  • 电脑作为服务器+数据库环境构建以及VS中C#远程连接数据库

    一 将作为服务器的电脑连接网络 xff08 以下简称 服务器 xff09 xff0c 查询服务器的ip地址 方法 xff1a cmd命令ipconfig中IPv4地址就是服务器的ip地址 二 测试服务器和用户电脑 xff08 就是另一台电脑
  • t和printf的缓冲机制

    众所周知 xff0c cout和buffer都是有缓冲的 网上很多把cout和printf混用出错归结为一个有缓冲 xff0c 一个无缓冲 xff0c 事实会在下面说明 cout和printf的输出是先从右往左读入缓冲区 xff0c 再从t
  • 天神降临,大家过来膜拜吧! FLASH AS 3.0 A星(A*, A star) 寻路算法--史上最快,极限优化挑战!

    天神降临 xff0c 大家过来膜拜吧 oh yeah 转载请声明出处 xff0c 例子代码可以免费随意使用 xff0c 但请保留或注明作者信息 这里的算法说是终极优化 我挑战了一下 http eidiot net 2007 04 17 a
  • redis master和slave主备切换,可能导致数据丢失,如何解决?

    1 两种数据丢失的情况 2 解决异步复制和脑裂导致的数据丢失 1 两种数据丢失的情况 主备切换的过程 xff0c 可能会导致数据丢失 xff08 1 xff09 异步复制导致的数据丢失 因为master gt slave的复制是异步的 xf
  • C:基于可以自动扩展缓冲区的stringbuffer,实现内存格式化输出(bufprintf)

    最近做一个C语言的嵌入式项目 xff0c 需要分段向指定内存调用vsnprintf输出不定长度的格式化输出 xff0c 因为是分段输出 xff0c 而且长度不定 xff0c 所以一开始就不能分配固定长度内存 xff0c 每次输出都要从输出到
  • Gitblit服务器搭建及Git使用

    使用Gitblit搭建属于公司或自己的Git服务器 xff0c 方便公司或自己程序代码及文档版本管理 环境 xff1a 1 Win10 64位操作系统 2 Git 2 24 1 2 64 bit xff08 git工具 xff09 3 To
  • C语言中int到float的强制类型转换

    最近在看一本名为的书 由于我所看过的计算机理论方面的书较少 xff0c 加上自己大学期间一直也不用功 xff0c 所以对于计算机的工作原理以及程序的工作方式我始终只知甚少 xff0c 印象也十分模糊 不过 xff0c 应该说我碰到了一本好书
  • 非常实用的一键开关机电路

    按键电路在我们的电路设计中非常常见 xff0c 其中有一种比较特殊 xff0c 就是一键开关机电路 xff0c 顾名思义 xff0c 就是只用一个按键实现开机关机以及其他功能 xff0c 其实大家都接触过 xff0c 我们手机中的开机键就是
  • 一个很精妙的高精度电压基准电路

    先上图 xff0c 图里面的431也可以是别的基准源 xff08 比如LT1004之类的 xff09 甚至可以是一个简单的稳压二极管 需要说明的时 xff0c 此电路并非本人原创 xff0c 也不知道作者是谁 xff0c 偶然看到后 xff
  • AD拼板技巧

    随着整个电子产业的不断发展 xff0c 电子行业的很多产品都已经有完善的上下游配套企业 从一个成熟产品的方案设计 xff0c 外观设计 xff0c 加工制造 xff0c 装配测试 xff0c 包装 xff0c 批发商渠道等等 xff0c 这
  • 单片机RS485通信接口、控制线、原理图及程序实例

    RS232 标准是诞生于 RS485 之前的 xff0c 但是 RS232 有几处不足的地方 xff1a 接口的信号电平值较高 xff0c 达到十几 V xff0c 使用不当容易损坏接口芯片 xff0c 电平标准也与TTL 电平不兼容 传输
  • AD圆形铺铜技巧

    1 在铺铜时按shift 43 空格是可以画圆弧 xff0c 但那只能画一个很小的圆 xff0c 可以用 34 34 34 34 xff08 逗号 xff0c 句号键 xff09 来调整圆的半径 选择铺铜命令 gt 设置参数 gt 进入铺铜
  • 解决STM32 I2C接口死锁在BUSY状态的方法讨论

    解决STM32 I2C接口死锁在BUSY状态的方法讨论 关于STM32的I2C接口死锁在BUSY状态无法恢复的现象 xff0c 网上已有很多讨论 xff0c 看早几年比较老的贴子 xff0c 有人提到复位MCU也无法恢复 只有断电才行的状况
  • 解决AD不能导入CAD文件

    相信好的小伙伴在导入Auto CAD文件时出现下面图片所示的文件后缀 xff0c 且只有这一种 xff1a 那怎么解决呢 xff1f xff1f xff1f 这是因为你没有安装插入的插件而已 xff0c 实际上这是由于新的安装机制导致 xf
  • do{...}while(0)的用法,超详解

    转载原文地址 xff1a http blog csdn net majianfei1023 article details 45246865 零 导引 第一次见到 do while 0 是在学习libevent的时候 xff0c 看到里面有