do...while(FALSE)的两个妙用

本文探讨了在C++编程中如何使用do...while(FALSE)循环替代goto语句以提高代码质量和可读性,并展示了在宏定义中使用该结构解决实际问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、使用do...while(FALSE)消除goto语句。

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

Sample I:

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:

Sample II:

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

Sample III:

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;

   

}

哈哈,相对比之下,使用do...while(FALSE)的妙处不言而喻了。

二、宏定义中的do...while(FALSE)

  在MFCafx.h文件里面, 你会发现很多宏定义都是用了do...while(0)do...while(FALSE), 比如说:

#define AFXASSUME(cond)       do { bool __afx_condVal=!!(cond); ASSERT(__afx_condVal); __analysis_assume(__afx_condVal); } while(0) 

粗看我们就会觉得很奇怪,既然循环里面只执行了一次,我要这个看似多余的do...while(0)有什么意义呢? 

当然有!

为了看起来更清晰,这里用一个简单点的宏来演示:

#define SAFE_DELETE(p) do{ delete p; p = NULL} while(0)

假设这里去掉do...while(0),

#define SAFE_DELETE(p) delete p; p = NULL;

那么以下代码:

if(NULL != p) SAFE_DELETE(p)

else   ...do sth...

就有两个问题,

1) 因为if分支后有两个语句,else分支没有对应的if,编译失败

2) 假设没有else, SAFE_DELETE中的第二个语句无论if测试是否通过,会永远执行。

你可能发现,为了避免这两个问题,我不一定要用这个令人费解的do...while,  我直接用{}括起来就可以了

#define SAFE_DELETE(p) { delete p; p = NULL;}

的确,这样的话上面的问题是不存在了,但是我想对于C++程序员来讲,在每个语句后面加分号是一种约定俗成的习惯,这样的话,以下代码:

If(NULL != p) SAFE_DELETE(p);

else   ...do sth...

else分支就无法通过编译了(原因同上),所以采用do...while(0)是做好的选择了。

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

if(...) 

{

}

else

{

}

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

<think>我们被要求解释并实现一个使用`do...while`循环的示例。`do...while`循环是一种后测试循环,即循环体至少会执一次,然后在循环的末尾检查条件。如果条件为真,则继续执循环;否则退出循环。 下面我们将通过几个示例来展示`do...while`循环的用法,并解释代码。 示例1:简单的数字输入验证 要求用户输入一个1到10之间的数字,如果输入无效则提示重新输入,直到输入有效为止。 示例2:计算数字的各位数之和,直到结果为一位数(即求数字根) 例如:输入123,则1+2+3=6,结果为6。如果输入99,则9+9=18,然后1+8=9,结果为9。 示例3:使用`do...while`实现菜单驱动程序 我们将用C语言实现这些示例,因为C语言中`do...while`很常见。但请注意,其他语言如Java、C++等也支持类似的语法。 让我们开始第一个示例:</think>### 深入解析 `do...while` 循环:语法、应用场景与最佳实践 `do...while` 循环是一种后测试循环结构,它**先执循环体,再检查循环条件**。这种结构确保循环体至少执一次,即使循环条件初始就不满足。 #### 基本语法 ```c do { // 循环体代码 } while (condition); ``` #### 与 `while` 循环的关键区别 | 特性 | `while` 循环 | `do...while` 循环 | |------|--------------|-------------------| | **条件检查位置** | 循环体执前 | 循环体执后 | | **最少执次数** | 0次 | 1次 | | **适用场景** | 条件可能初始不成立 | 必须至少执一次 | --- ### 实际应用场景与代码示例 #### 场景1:用户输入验证(必须至少获取一次输入) ```c #include <stdio.h> #include <ctype.h> int main() { char choice; do { printf("\n菜单选项:\n"); printf("a) 保存文件\n"); printf("b) 加载文件\n"); printf("c) 退出程序\n"); printf("请选择: "); scanf(" %c", &choice); // 注意空格跳过空白字符 choice = tolower(choice); switch(choice) { case 'a': printf("保存文件...\n"); // 保存逻辑 break; case 'b': printf("加载文件...\n"); // 加载逻辑 break; case 'c': printf("退出程序...\n"); break; default: printf("无效选项! 请重试.\n"); } } while (choice != 'c'); // 直到用户选择退出 return 0; } ``` #### 场景2:处理数据直到满足条件(文件/网络流处理) ```c #include <stdio.h> #define BUFFER_SIZE 256 void process_stream(FILE *stream) { char buffer[BUFFER_SIZE]; int bytes_read; int total_bytes = 0; do { bytes_read = fread(buffer, 1, BUFFER_SIZE, stream); total_bytes += bytes_read; // 处理数据块 printf("处理了 %d 字节数据\n", bytes_read); // 实际数据处理逻辑... } while (bytes_read == BUFFER_SIZE); // 直到读取不完整块 printf("总计处理: %d 字节\n", total_bytes); } int main() { FILE *file = fopen("data.bin", "rb"); if (!file) { perror("打开文件失败"); return 1; } process_stream(file); fclose(file); return 0; } ``` #### 场景3:游戏循环(必须至少更新一帧) ```c #include <stdbool.h> bool is_game_running = true; int frame_count = 0; void game_loop() { do { // 1. 处理输入 process_input(); // 2. 更新游戏状态 update_game_state(); // 3. 渲染画面 render_frame(); frame_count++; // 检查退出条件 if (should_exit_game()) { is_game_running = false; } } while (is_game_running); printf("游戏结束! 总帧数: %d\n", frame_count); } ``` --- ### 高级应用:带错误处理的重试机制 ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #define MAX_RETRIES 5 #define RETRY_DELAY 1000 // 毫秒 typedef enum { SUCCESS, TRANSIENT_ERROR, PERMANENT_ERROR } OperationStatus; // 模拟可能失败的操作 OperationStatus sensitive_operation() { // 30% 成功, 50% 临时错误, 20% 永久错误 int r = rand() % 10; if (r < 3) return SUCCESS; if (r < 8) return TRANSIENT_ERROR; return PERMANENT_ERROR; } int main() { srand(time(NULL)); // 初始化随机种子 int retry_count = 0; OperationStatus status; do { status = sensitive_operation(); retry_count++; switch(status) { case SUCCESS: printf("操作成功! (尝试次数: %d)\n", retry_count); break; case TRANSIENT_ERROR: if (retry_count < MAX_RETRIES) { printf("临时错误 (尝试 %d/%d), %d毫秒后重试...\n", retry_count, MAX_RETRIES, RETRY_DELAY); usleep(RETRY_DELAY * 1000); // 微秒 } break; case PERMANENT_ERROR: printf("永久错误! 无法恢复\n"); break; } } while (status == TRANSIENT_ERROR && retry_count < MAX_RETRIES); if (status != SUCCESS) { printf("操作最终失败\n"); return 1; } return 0; } ``` --- ### 跨语言比较 #### Java 实现 ```java import java.util.Scanner; public class DoWhileExample { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int number; do { System.out.print("请输入正整数 (0退出): "); number = scanner.nextInt(); if (number > 0) { System.out.println("输入值: " + number); } else if (number < 0) { System.out.println("错误:请输入正数!"); } } while (number != 0); System.out.println("程序结束"); scanner.close(); } } ``` #### Python 实现(通过 `while True` + `break` 模拟) ```python import random # 模拟 do...while def retry_operation(): max_retries = 3 attempts = 0 while True: # 模拟 do 部分 attempts += 1 result = random.choice([True, False]) # 50% 成功率 if result: print(f"成功! 尝试次数: {attempts}") break if attempts >= max_retries: print(f"失败! 已达最大尝试次数: {max_retries}") break print(f"尝试 {attempts} 失败,重试中...") return result if __name__ == "__main__": retry_operation() ``` --- ### 最佳实践与注意事项 1. **最少执保证**:当操作必须至少执一次时使用 ```c // 必须至少发送一次请求 do { send_request(); } while (!response_received()); ``` 2. **避免无限循环**:确保条件最终会变为假 ```c int counter = 10; do { // 错误:条件永远为真! } while (counter > 0); // 缺少 counter-- 会导致无限循环 ``` 3. **状态机实现**:处理多步骤流程 ```c typedef enum { INIT, PROCESSING, DONE } State; State current = INIT; do { switch(current) { case INIT: setup_resources(); current = PROCESSING; break; case PROCESSING: if (process_data()) current = DONE; break; } } while (current != DONE); ``` 4. **与 break/continue 配合**: ```c do { if (skip_condition) continue; // 跳过本次迭代 if (exit_condition) break; // 提前退出循环 // 正常处理 } while (condition); ``` 5. **性能考量**: - 当循环体大且条件复杂时,`do...while` 可能比 `while` 更高效 - 现代编译器通常能优化两种循环为相同机器码 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值