C/C++中do...while(0)的妙用

C++中do...while(0)的妙用 一点睛 do{...}while(condition)可以表示循环,但你有没有遇到在一些地方宏定义中可以不用循环的地方,也用到了do{...}while(condition),比如有这样的宏: #define Foo(x) do{\ printf("first statement\n");\ printf("second statement\n");\ }while(0) ... 阅读详情


【本文转载了2篇blog关于do...while(0)的巧妙用法,这两篇文章内容水平很高,在此谢谢原文作者,如需转载请保留原文作者的连接(而不是我的连接)。原文如下:

http://www.devbean.net/2014/01/do-while-in-macro/

http://www.cppblog.com/SmartPtr/archive/2007/07/03/27443.html

】


参考链接:http://www.pixelstech.net/article/1390482950-do-%7B-%7D-while-%280%29-in-macros

虽然我们不建议使用宏,但是,作为一个语言特性,有时宏是不可避免的。对于这种情况,正确使用宏尤其重要。它可以帮助你减少很多重复性工作。但是,如果你没有仔细地定义,宏绝对能把你逼疯。在很多 C/C++ 程序中,你会看到类似如下定义的宏:

#define __set_task_state(tsk, state_value)      \
    do { (tsk)->state = (state_value); } while (0)

宏的语句被do{...}while(0)包围起来。在很多 C 库,包括 Linux 内核中,这种情形都不少见。这种红如何使用?来自 Google 的 Robert Love(他曾经是 Linux 内核开发人员)给了我们答案。

do{...}while(0)是 C 语言中唯一能让宏后面加上分号还能够正确工作的语法结构。使用do{...}while(0)包围起来,我们就不需要担心宏被如何使用。例如:

#define foo(x)  bar(x); baz(x)

之后,我们这样调用:

foo(wolf);

宏将被展开为

bar(wolf); baz(wolf);

这正是正确的结果。下面,我们来看另外一种情况:

if (!feral)
    foo(wolf);

此时,宏展开结果为:

if(!feral)
    bar(wolf);
baz(wolf);

这很可能不是你所期望的。

也就是说,我们所定义的这个宏并不适用于所有使用它的情形。没有do{...}while(0)语句,我们不可能写出类似函数的宏。

如果我们将这个宏使用do{...}while(0)语句重新定义,那么就会是这样的:

#define foo(x)  do { bar(x); baz(x); } while (0)

现在,这个语句与之前的那个在功能上是等价的。通过将语句嵌于花括号之内,我们保证了整个逻辑不会被宏外面的分号打断;while(0)则保证这个逻辑片段只执行一次,这和没有循环是一致的。使用改进过的宏,上述语句会被展开为:

if(!feral)
    do{bar(wolf);baz(wolf);}while(0);

语义上说,它等价于:

if (!feral) {
    bar(wolf);
    baz(wolf);
}

或许你会问,为什么不仅仅使用一对花括号就好了?为什么必须要有do{...}while(0)?例如,我们这样定义宏:

#define foo(x)  { bar(x); baz(x); }

如此定义,上面的展开语句仍然正确。但是对于C++程序员来讲,在么个语句后面加分号是一个约定成俗的习惯,这样的话,我们考虑另外一种情况:

if (!feral)
    foo(wolf);
else
    bin(wolf);

这种写法将被展开为:

if(!feral){
    bar(wolf);
    baz(wolf);
};
else
    bin(wolf);

现在我们有了一个语法错误,所以采用do...while(0)是最好的选择了。

也许你会说,我们代码的习惯是在每个判断后面加上{}, 就不会有这种问题了,也就不需要do...while了,如:

if( ... )
{
	... // do sth.
	foo(x);
        
}
else 
{
	...//do sth.
}

诚然,这是一个好的,应该提倡的编程习惯,但一般这样的宏都是作为library的一部分出现的,而对于一个library的作者,他所要做的就是让其库具有通用性,强壮性,因此他不能有任何对库的使用者的假设,如其编码规范,技术水平等。

总结一下,在 Linux 或其它代码库中的宏使用do{...}while(0)包围其逻辑,这保证了宏在使用时,不管宏所在的位置花括号、分号如何写,宏的表现总是一致的。


####################################################################################################################################

【以下内容来自:http://www.cppblog.com/SmartPtr/archive/2007/07/03/27443.html】 

在C++中,有三种类型的循环语句:for, while, 和do...while, 但是在一般应用中作循环时, 我们可能用for和while要多一些,do...while相对不受重视。
    但是,最近在读我们项目的代码时,却发现了do...while的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。

1. do...while(0)消除goto语句。
通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:
version 1

bool Execute()
{
   // 分配资源
   int *p = new int;
   bool bOk(true);

   // 执行并进行错误处理
   bOk = func1();
   if(!bOk) 
   {
      delete p;   
      p = NULL;
      return false;
   }

   bOk = func2();
   if(!bOk) 
   {
      delete p;   
      p = NULL;
      return false;
   }

   bOk = func3();
   if(!bOk) 
   {
      delete p;   
      p = NULL;
      return false;
   }

   // .

   // 执行成功,释放资源并返回
    delete p;   
    p = NULL;
    return true;
   
}

这里一个最大的问题就是代码的冗余,而且我每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto:
version 2
bool Execute()
{
   // 分配资源
   int *p = new int;
   bool bOk(true);

   // 执行并进行错误处理
   bOk = func1();
   if(!bOk) goto errorhandle;

   bOk = func2();
   if(!bOk) goto errorhandle;

   bOk = func3();
   if(!bOk) goto errorhandle;

   // .

   // 执行成功,释放资源并返回
    delete p;   
    p = NULL;
    return true;

errorhandle:
    delete p;   
    p = NULL;
    return false;
   
}

代码冗余是消除了,但是我们引入了C++中身份比较微妙的goto语句,虽然正确的使用goto可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto语句,又能消除代码冗余呢,请看do...while(0)循环:
version3
bool Execute()
{
   // 分配资源
   int *p = new int;

   bool bOk(true);
   do
   {
      // 执行并进行错误处理
      bOk = func1();
      if(!bOk) break;

      bOk = func2();
      if(!bOk) break;

      bOk = func3();
      if(!bOk) break;

      // .

   }while(0);

    // 释放资源
    delete p;   
    p = NULL;
    return bOk;
   
}


“漂亮!”, 看代码就行了,啥都不用说了...



C/C++之while(do-while)详细讲解 图 1 显示了 while 循环的一般格式,右侧流程图直观地描绘了其工作原理。图 1 while 循环的语句结构及流程图现在来看一看 while 循环的每个部分。第一行,有时称为循环头,由关键字 while 组成,后跟待测试的条件,括在括号中。条件由任何可被判断为 true 或 false 的表达式表示。接下来是循环体,它包含一个或多个 C++语句。循环的工作原理是,首先判断条件表达式的值,如果它是真的,则循环体中的每个语句都被执行; 阅读详情

相关推荐

OpenEarthMap 与 xBD 数据集合并

整合好的数据:链接: https://pan.baidu.com/s/1uJ_Zg6n-s3u1D49OW52D7Q?pwd=2vwx 提取码: 2vwx完整整合步骤与代码如下。

qq_60293814的博客 1593

c / c++ 中 do {} while(0) 的 2 种用法

do {} while(0) 常见的两种用法 1.宏定义的多行定义; 2.代码的跳转,使用break跳过某些代码块。

YanDabbs的博客 564

BERT之提取特征向量 及 bert-as-server的使用

本文内容列表提取句向量1、句向量简介1-1传统句向量1-2、BERT句向量2、 extract_features.py源码分析2-1 main函数 前一篇文章 BERT介绍及中文文本相似度任务实践 简单介绍了使用BERT进行中文文本相似度计算的方法,这篇文章着重对特征提取方法进行讲述。 提取句向量 1、句向量简介 1-1传统句向量 更多采用word embedding的方式取加权平均,该方法的一大...

ling620的专栏 1万+

09 C++ 中的循环(while循环、do-while循环、for循环及嵌套循环)

C++中的循环指的是重复执行一段程序代码的结构。循环语句可以让程序重复执行某一段代码,直到满足某一条件,或者达到某一次数的执行次数。常见的循环语句有while循环、do-while循环和for循环。循环语句是C++语言中基本的控制结构之一,可以帮助程序员编写高效、简洁的程序。下面让我们来逐个学习。一、下面是使用C++ 中的循环注意事项总结:循环条件的控制:循环条件必须在循环内部得到更新和控制,否则将可能出现无限循环的情况。循环范围的控制:循环体内的代码必须正确、清晰地控制循环范围。

m0_51816252的博客 3185

《玩儿起来吧》MATLAB 实时图像处理系列(一二三四)

大家学了图像处理之后,是不是就想学以致用。而要用于生活中,最好的方式就是和摄像头搭配,实现实时图像处理。今天就给大家分享MATLAB实时图像处理的基础方法,希望能帮你快速入门。

HPC_ZY 1万+

智能车竞赛疯狂电路组:Altium Designer赛道设计与计分策略全解析

本文全面解析智能车竞赛疯狂电路组中,如何利用Altium Designer进行专业赛道设计。从软件基础操作、图层管理与封装库调用,到赛道拓扑绘制、核心尺寸规范,详细拆解设计全流程。文章深入结合计分策略,指导如何在设计阶段植入策略点,优化路径与元器件布局,以在比赛中获得时间优势,并提供了从图纸输出到实地测试的完整避坑指南。

beta5的博客 583

c++教学(8)while循环

计数器 while死循环 break。

Happy_potter_RD的博客 8773

【100%通过率】华为od机试C卷【出差 / 员工派遣】Java&Go 实现

某公司部门需要派遣员工去国外做项目。现在,代号为 x 的国家和代号为 y 的国家分别需要 cntx 名和 cnty 名员工部门每个员工有一个员工号 (1,2,3,......),工号连续,从 1开始。部长派遣员工的规则:

MISAYAONE的博客 2638

C++基础语法:while的使用

"打牢基础,万事不愁" .C++的基础语法的学习."学以致用,边学边用",编程是实践性很强的技术,在运用中理解,总结.

jllws1的博客 5466

2025年微信小程序获取手机号授权登录注册详细教程(含服务端)。不弹窗原因以及减少组件用量的方法

前端把获取到的code传给后端,后端获取access_token和前端传过来的code,请求服务器接口,即可获取到手机号。

coderYYY的博客 3万+

SentEval学习笔记

SentEval allows you to evaluate your sentence embeddings as features for the followingdownstreamtasks: Task Type #train #test needs_train set_classifier MR movie review(影评) 11k 11k 1 1 CR product review(产品评述) ...

AlphaFinance 803

C/C++宏定义中do{}while(0)的妙用

摘要: do{}while(0)是C/C++中一种特殊编程技巧,主要用于宏定义和代码块封装。其核心价值包括: 宏安全性:确保多语句宏在条件语句中展开时语法正确,强制分号结尾,避免if-else断裂问题; 错误处理:替代goto实现结构化资源清理,通过break跳转和集中式回收简化多步骤操作的错误处理; 作用域隔离:创建临时块级作用域,限制变量生命周期,避免命名冲突; 代码一致性:保持与函数调用相同的分号习惯,提升可维护性。该技巧在宏定义、嵌套错误处理及资源管理中具有不可替代的优势。

张佳航的博客 1261

C++11、C++14 和 C++17 标准新特性详解

特性C++11C++14C++17auto类型推导✅范围 for 循环✅Lambda 表达式✅✅✅智能指针✅移动语义✅nullptr✅统一初始化{}✅✅constexpr✅✅✅✅数字分隔符✅结构化绑定✅✅✅文件系统库✅。

h a 6 6 6 c k 为 你解决复杂难题! 439

C/C++: do...while(0)的妙用

在C++中,有三种类型的循环语句:for, while, 和do…while, 但是在一般应用中作循环时, 我们可能用for和while要多一些,do…while相对不受重视。 但是,最近在读我们项目的代码时,却发现了do…while的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。 do…while(0)消除goto语句。 ...

Z_Paper的博客 201

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Linux c/c++ do{...}while(0)的妙用

前言 最近用man手册查看API函数时,常见例子中含有do{…}while(0)奇怪的用法。为什么要用do{…}while(0)呢?就是能够实现复杂的定义,通用性强,任何情况下都适用。 do{…}while(0)的妙用 下面do{…}while(0)是Linux内核中常见用在错误的处理上: #define handle_error_en(en, msg) \ do { errno =...

chen1415886044的博客 1145

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

c/c++中的do while(0)和##粘贴符的妙用

do whlie是高中就学过的循环体 while(0) 表示在终止循环前始终都会先运行一次循环体, 用define封装函数的时候经常可以用到这个技巧。 另外一个技巧就是粘贴符##的使用。 #include<iostream> #include<stdio.h> using namespace std; #define paster(n) printf("tok...

legalhighhigh的博客 353

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Linux c/c++编程--知识点(1) #define 和 do{...}while(0) 的妙用

文章原文:https://blog.csdn.net/tianxieeryang/article/details/86687065 返回知识列表: 为什么要这么用,举一个错误例子示范就可以了 void print1() { printf("Hello 1"); } void print2() { printf("Hello 2"); } #define func() print1();print2(); int main() { if (0) func();

编程到管理、技术到产品 342

C/C++ 中的 do{}while(0):看似多余的实用技巧

do{}while(0)是C/C++中一个看似多余实则实用的技巧。它主要用于宏定义中封装多语句,确保宏在各种使用场景下都能正确展开,避免语法错误。相比普通代码块,它能自然地以分号结束,符合编码习惯。该技巧还可用于简化条件跳转、结构化资源管理,确保资源释放。虽然单次执行,但能解决宏展开可能导致的if-else不匹配问题,使代码更健壮。合理使用这一技巧能提升代码的可靠性和可维护性。

bkspiderx的博客 1236

do{...}while(0)的用法

do{...}while(0) 的妙用 1.导引: 第一次见到 do{...}while(0)是在学习libevent的时候,看到里面有很多类似 #define TT_URI(want) do { \ char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \ tt_want(ret != NULL);

【码农在新加坡】的专栏 2万+

关于do{}while(0)

do…while(0)的用法 这样的宏见过么: Cpp代码: #define FOO(x) do {\ some_code_line_1;\ some_code_line_2;\ } while (0) (注意while(0)后面没有分号) 乍一看这个宏不过就是定义了个包含多个语句的local scope,用do…while(0)包围起来简直多此一举。直接这样不就好了么: Cpp代码: #defi...

qq_42263831的博客 269

C/C++预处理指令和宏定义#define及 do while(0)

本文主要记录了C/C++预处理指令 #define 宏定义 do/while(0) 什么时候使用

一丁的博客 1161

do{...}while(0)

linux内核和其他一些开源的代码中,经常会遇到这样的代码: do{ ... }while(0) 这样的代码一看就不是一个循环,do…while表面上在这里一点意义都没有,那么为什么要这么用呢? 实际上,do{…}while(0)的作用远大于美化你的代码。查了些资料,总结起来这样写主要有以下几点好处: 1、辅助定义复杂的宏,避免引用的时候出错: 2、避免使用goto对程序流进行统一的控制: 3、避免空宏引起的warning 4、定义一个单独的函数块来实现复杂的操作: [参考资料] [1]《do{…}

_OpenRed_的博客 259

C/C++之do while循环

while循环和for循环都是入口条件循环,即在循环的每次迭代之前检查测试条件,所以有可能根本不执行循环体中的内容。C语言还有出口条件循环(exit-condition loop),即在循环的每次迭代之后检查测试条件,这保证了至少执行循环体中的内容一次。这种循环被称为do while循环。如下程序演示了一个示例。 #include <stdio.h> int main(void) { const int secret_code = 13; int code_en...

专注嵌入式开发与 Linux 系统应用实战,分享芯片驱动、设备调试、系统工具开发干货。 6121

C/C++的奇技淫巧之 do{}while(0)

do{}while(0)()do{}while(0)绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法并不会导致程序的性能降低。另外,GCC提供了Statement-Expressions用以替代do{…}while(0);所以在GCC中也可以这样定义宏:注意,MSVC不支持这种语义。

u014677109的博客 930
上一篇: stdout与stderr的区别
下一篇: C语言中关于获取时间的函数,包括如果获取微妙、毫秒级时间
code_robot
博客等级 码龄17年 14粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值