首页 > 编程语言 >C++惯用法:do...while(0)的妙用

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

时间:2024-07-14 19:01:40浏览次数:21  
标签:do goto ... int C++ while bOk

目录

1.引言

2.do...while(0)消除goto语句

3.用do...while(0)包裹复杂的宏

4.防止意外错误

5.避免变量作用域问题


1.引言

        在C++中,do...while(0) 通常是用来做循环用的,然而我们做循环操作可能用for和while要多一些。经常看到一些开源代码会出现do...while(0)这样的代码,这样的代码看上去肯定不是用来做循环的,那为什么要这样用呢?下面就讲讲使用它的好处。

2.do...while(0)消除goto语句

        通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:

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:

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)循环:

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;
}

3.用do...while(0)包裹复杂的宏

假设定义了一个这样的宏:

#define SWAP(a, b) \
    int temp = a; \
    a = b; \
    b = temp;

如果直接在代码中使用 SWAP(x, y);,可能会导致意料之外的行为,特别是在条件语句中:

if (condition)
    SWAP(x, y);
else
    // 其他操作

上面的代码将展开为:

if (condition)
    int temp = x; x = y; y = temp;
else
    // 其他操作

这将导致编译错误,因为 else 部分并没有与 if 关联。为了避免这种问题,可以使用 do-while 包裹:

#define SWAP(a, b) \
    do { \
        int temp = a; \
        a = b; \
        b = temp; \
    } while (0)

这样展开后,代码将变为:

if (condition)
    do { int temp = x; x = y; y = temp; } while (0);
else
    // 其他操作

这样就保证了 if 和 else 结构的完整性,不会出现编译错误。

实际案例

为了更好地理解 do-while 包裹宏的好处,让我们看看一个实际的示例。假设我们需要编写一个宏来打印调试信息:

#define DEBUG_PRINT(msg) \
    do { \
        printf("DEBUG: %s\n", msg); \
    } while (0)

在调试模式下,我们可以安全地使用这个宏:

if (debugMode)
    DEBUG_PRINT("Debugging information");
else
    printf("Normal operation\n");

        由于使用了 do-while 包裹,DEBUG_PRINT 宏将安全地作为一个单独的块执行,不会影响 if-else 结构。

  do{...}while(0);在宏定义中的妙用在于它能够确保宏的行为符合预期,特别是在复杂的控制流语句中。它提供了一个清晰的方式来定义宏,使得宏的调用看起来和感觉起来都像是单条语句,无论宏内部包含多少条实际的语句。这种技术有助于提高代码的可读性和可维护性,并减少因宏展开而导致的潜在错误。

4.防止意外错误

        如果没有使用do...while(0)或其他形式的封装,宏展开后的多条语句可能会因为缺少分号、括号不匹配等原因导致编译错误。此外,如果宏被用在需要单条语句的地方,而宏内部有多条语句且没有适当的封装,那么忘记在宏调用后加分号可能会影响到后续的代码。

5.避免变量作用域问题

当你的功能很复杂,变量很多你又不愿意增加一个函数的时候,使用do{}while(0);,将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。例如:

#define MAX(a, b) \
    do { \
        int _a = (a); \
        int _b = (b); \
        (void)((_a > _b) ? _a : _b); \
    } while (0)

这里 _a 和 _b 仅在 do-while 块内有效,防止了变量名冲突。

标签:do,goto,...,int,C++,while,bOk
From: https://blog.csdn.net/haokan123456789/article/details/140414636

相关文章

  • 【SQLServer备份策略】通过Windows任务计划程序清理SQLServer历史备份
    服务器管理器->工具->任务计划程序右键"任务计划程序"->新文件夹,自定义为DBA右键DBA文件夹,创建基本任务,命名为purge_backup_history选择每天触发选择每天的执行时间为2:00:00,选择启动程序。因为数据库备份策略在每天23:00,所以指定清理历史备份时间为凌晨2点导入清理脚本......
  • [C++]哈希
    一、概念在顺序结构以及平衡树中,元素关键码(key)与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码(key)的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(log2N),搜索的效率取决于搜索过程中元素的比较次数。理想的搜索方法:可以不经过任何比较,一......
  • 封装C++项目为dll
    这是头文件,定义了一个接口类IMyInterface。#pragmaonce#ifndefMY_INTERFACE_H#defineMY_INTERFACE_H#define_CRT_SECURE_NO_WARNINGS#defineMY_DLL_API__declspec(dllexport)//定义导出到DLL中的宏//接口类,用于导出到DLLclassMY_DLL_APIIMyInterface{pub......
  • DOS命令行
    DOS命令行CMD的方式开始+系统+命令提示符Win键+R输入CMD打开控制台适用任意文件夹下面按住Shift+鼠标右击点击+在此处打开命令行窗口在资源管理器地址栏加上CMD路径管理员方式运行:选择以管理员方式运行常用的DOS命令盘符切换:D:查看文件所有目录:dir切换......
  • C++嵌入式压缩库bundle基础操作:内存压缩与zip文件操作
    bundle是一个轻量级的C++压缩库,集成在一对简洁的文件中(bundle.h和bundle.cpp),支持内存数据的压缩与解压缩,以及zip格式文件的操作,方便嵌入到C++程序中执行压缩、解压缩操作。本文将详细介绍如何使用bundle库进行高效的数据压缩处理。简介bundle库支持多种压缩算法,使用std::string......
  • C#面:dot net core管道里面的map拓展有什么作用?
    在.NETCore管道中,Map拓展方法用于将中间件添加到请求处理管道中。它的作用是根据请求的路径或其他条件来选择性地执行中间件。具体来说,Map方法接受一个路径参数和一个委托参数。当请求的路径与指定的路径匹配时,该委托中的中间件将被执行。这使得我们可以根据不同的路径来应用......
  • Windows的常用快捷键
    Windows的常用快捷键键盘功能键Tab:切换键Shift:功能组合、转换键Ctrl:控制键Alt:组合键空格:Enter:换行Windows键:系统菜单键键盘快捷键Ctrl+C:复制Ctrl+V:粘贴Ctrl+A:全选Ctrl+X:剪切Ctrl+Z:撤销操作Alt+F4:关闭窗口Ctrl+Shift+ESC:启动......
  • 扩展Windows Server 2022的远程桌面连接数
    一、添加远程桌面授权服务打开服务器管理器:登录到WindowsServer2022服务器。点击“开始”菜单,搜索“服务器管理器”并打开它。启动“添加角色和功能”向导:在服务器管理器中,点击左侧菜单中的“管理”然后选择“添加角色和功能”。点击“下一步”继续。选择安装......
  • 高质量C/C++编程指南总结(四)—— 表达式和基本语句
    1.运算符优先级如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。 2.复合表达式不要编写太复杂的复合表达式。不要有多用途的复合表达式。不要把程序中的复合表达式与“真正的数学表达式”混淆。 3.if语句不可将布尔变量直接与 ......
  • 高质量C/C++编程指南总结(三)—— 命名规则
    标识符应当直观,可望文知义。标识符的长度应当符合“min-length&& max-information”原则。命名规则尽量与所采用的操作系统或开发工具的风格保持一致。程序中不要仅靠大小写区分相似的标识符。程序中不要出现标识符完全相同的局部变量和全局变量。变量的名字应当使用“......