do{}while(0)的妙用

本文介绍了do...while(0)在C++中的巧妙应用,包括消除goto语句提升代码健壮性,以及在宏定义中使用do...while(0)解决编译问题。通过具体代码示例展示了其在错误处理和资源管理中的作用。

do...while(0)的妙用

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

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

bool Execute()
{
   
//分配资源

   int *p = new int" _msthash="1428141" _mstchunk="true">int *p = new int ;
   
bool bOk(true" _msthash="1428142" _mstchunk="true">bool bOk(true
);

   
//执行并进行错误处理

bOk = func1();
   
if(!
bOk)
   {
delete p;
p
=
NULL;
      
return false" _msthash="1462539" _mstchunk="true">return false
;
   }

bOk
=
func2();
   
if(!
bOk)
   {
delete p;
p
=
NULL;
      
return false" _msthash="1465334" _mstchunk="true">return false
;
   }

bOk
=
func3();
   
if(!
bOk)
   {
delete p;
p
=
NULL;
      
return false" _msthash="1463085" _mstchunk="true">return false
;
   }

   
//
 ..........

   
//执行成功,释放资源并返回

delete p;
p
=
NULL;
    
return true" _msthash="1465321" _mstchunk="true">return true
;
   
}


这里一个最大的问题就是代码的冗余,而且我每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto:
version 2

bool Execute()
{
   
//分配资源

   int *p = new int" _msthash="1428143" _mstchunk="true">int *p = new int ;
   
bool bOk(true" _msthash="1428144" _mstchunk="true">bool bOk(true
);

   
//执行并进行错误处理

bOk = func1();
   
if(!bOk) goto" _msthash="1430325" _mstchunk="true">if(!bOk) goto
errorhandle;

bOk
=
func2();
   
if(!bOk) goto" _msthash="1430871" _mstchunk="true">if(!bOk) goto
errorhandle;

bOk
=
func3();
   
if(!bOk) goto" _msthash="1461421" _mstchunk="true">if(!bOk) goto
errorhandle;

   
//
 ..........

   
//执行成功,释放资源并返回

delete p;
p
=
NULL;
    
return true" _msthash="1463657" _mstchunk="true">return true
;

errorhandle:
delete p;
p
=
NULL;
    
return false" _msthash="1464216" _mstchunk="true">return false
;
   
}


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

bool Execute()
{
   
//分配资源

   int *p = new int" _msthash="1428145" _mstchunk="true">int *p = new int ;

   
bool bOk(true" _msthash="1428146" _mstchunk="true">bool bOk(true
);
   
do

   {
      
//执行并进行错误处理
bOk = func1();
      
if(!bOk) break" _msthash="1430872" _mstchunk="true">if(!bOk) break
;

bOk
=
func2();
      
if(!bOk) break" _msthash="1461422" _mstchunk="true">if(!bOk) break
;

bOk
=
func3();
      
if(!bOk) break" _msthash="1461980" _mstchunk="true">if(!bOk) break
;

      
// ..........


   }
while(0" _msthash="1463098" _mstchunk="true">while(0 );

    
//释放资源

delete p;
p
=
NULL;
    
return
bOk;
   
}


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

2 宏定义中的do...while(0)
如果你是C++程序员,我有理由相信你用过,或者接触过,至少听说过MFC, 在MFC的afx.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的作者,他所要做的就是让其库具有通用性,强壮性,因此他不能有任何对库的使用者的假设,如其编码规范,技术水平等。

原文连接:http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html

1. What is an IDE (Integrated Development Environment), and what are its main components? 2. What is the role of a compiler in the C++ development process? 3. What is the difference between source code (e.g., a .cpp file) and an executable file? 4. In the "Hello, World!" program, what is the purpose of the line #include <iostream>? 5. What is special about the main() function in a C++ program? 6. Why do computers fundamentally operate using the binary (base-2) system? 7. What is the base of the hexadecimal system? Why is it often used by programmers as a shorthand for binary numbers? 8. Explain the "triad" method for converting an octal number to binary. 9. Briefly describe the "division by 2" method for converting a decimal number to binary. 10. What is the decimal value of the binary number 1011? 1. What is the purpose of the std::cout object? Which header file must be included to use it? 2.What is the difference between an escape sequence like \n and a manipulator like std::endl? (Hint: Both create a new line, but they have a subtle difference). 3.How would you print the following text to the console, including the quotes and the backslash: He said: "The file is in C:\Users\"? 4.Is it possible to write an entire multi-line text output using only one std::cout statement? If yes, how? 5.What is a syntax error? Give an example of a syntax error from Task 2. (Task 2: Debugging The following program contains several syntax errors. Copy the code into your IDE, identify the errors, fix them, and run the program to ensure it works correctly. Incorrect Code: */ Now you should not forget your glasses // #include <stream> int main { cout << "If this text" , cout >> " appears on your display, cout << " endl;" cout << 'you can pat yourself on ' << " the back!" << endl. return 0; "; ) Hint: Pay close attention to comments, header files, brackets ({}), operators (<<), semicolons, and how strings and manipulators are written.) 1. What is the difference between variable declaration and initialization? 2.What will be the result of the expression 7 / 2 in C++? Why? 3.What will be the result of the expression 10 % 3? What is the main purpose of the modulus operator? 4. What is the purpose of std::cin and the >> operator? 5. A beginner tries to swap two integer variables a and b with the code a = b; b = a;. Why will this not work correctly? 1. What is an algorithm? Name the primary ways to represent an algorithm. 2.List the main flowchart symbols and explain their purpose. 3.What are the three fundamental types of algorithm structures? Briefly describe each. 4.In a branching algorithm, what determines the flow of execution? 5.What is the key characteristic of a linear algorithm? 6.When is a cyclic algorithm structure used?7. 8. 9. 7.Explain the purpose of a connector in a flowchart. 8.What is the difference between a predefined process block and a standard process block? 9.In the context of solving a quadratic equation algorithm, what condition must be checked before calculating the roots? Why? 1. What are the three main approaches to data input and output offered by C++? 2. What is the purpose of the SetConsoleOutputCP(65001) and SetConsoleCP(65001)
functions in the provided C++ program example? 3. Explain the difference between the cin and cout objects in Stream 1/0. 4. When using formatted 1/0, which header file must be included to use manipulators like setw and setprecision? 5. List three manipulators used for data output in C++ and briefly describe what each one does. 6. In Formatted I/0 using printf), what are the conversion specifications for a decimal integer and a real number in exponential form? 7. What is the difference in how the & (address-of) operator is used when inputting a value for an integer variable versus a string variable using the scanf() function? 8. Which Character I/O function is used to output a single character to the screen, and which is used to output a string? 9. Describe the syntax and function of the ternary operator in C++. 10. What is the difference between the logical AND (&&) and logical OR (I|) operators when combining multiple conditions? 11. When is the default label executed in a C++ switch statement? 12. What is the primary purpose of the break statement within a switch block? 1. What is the main purpose of using loops in programming? 2. Explain the key difference between the for, while, and do while loops. 3. What happens if you forget to include the increment/decrement statement in a while loop? 4. How can you interrupt an infinite loop during program execution? 5. What is the role of the setw() and setfill) manipulators in C++? 6. In a nested loop, how does the inner loop behave relative to the outer loop? 7. What is type casting, and why is it used in loop calculations? 8. How does the do while loop differ from the while loop in terms of condition checking? 9. What output formatting options can be used to align numerical results in columns? 10*. How would you modify a loop to skip certain iterations based on a condition? 1. List the six main biwise operators in C++ and explain the function of each. 2. Why cannot bitwise operations be applied to variables of floating-point type? 3. Explain the purpose of the << (left shift) and >> (right shift) operators. What is the typical effect on the decimal value of a number when it is shifted left by 1? Shifted right by 1? 4. Describe the process of using a mask to check the value of a specific bit within an
integer. 5. How can you use the bitwise AND operator (&) to check if a number is even or odd?
Explain the logic. 6. What is the difference between the logical AND (&&) and the bitwise AND (&)? Provide an example scenario for each. 7. Explain the purpose of the ~ (bitwise NOT) operator. What is the result of applying it to a mask, and how can this be useful? 1. What is the primary goal of program debugging? What types of errors can it help identify? 2. Describe the difference between Step Over (F10) and Step Into (F11) debugging commands. When would you choose one over the other? 3. What is the purpose of a breakpoint in planned debugging? How do you set and remove a breakpoint in Visual Studio? 4. Explain the utility of the "Watch" window compared to the "Autos" or "Locals" windows during a debugging session. 5. What is the key difference between the Debug and Release configurations when building a project? Why is it necessary to create a Release version after successful debugging? 6. List at least three types of files commonly found in a project's Debug folder and briefly state their purpose (e.g., *.pdb). 7. During debugging, you notice a variable has an incorrect value. How can you change its value during runtime to test a hypothesis without modifying the source code? 8. What command is used to exit the debug mode and stop the current debugging session? 1. What is an array in C++? List its three main characteristics. 2. How are array elements numbered in C++? What is the valid index range for an array declared as int data[25];? 3. Explain the difference between array declaration and initialization. Provide an example of each. 4. What is an initializer list? What happens if the initializer list is shorter than the array size? 5. How can you let the compiler automatically determine the size of an array during initialization? 6. What values do elements of a local array contain if it is declared but not explicitly initialized? How does this differ from a global array? 7. What is an array out-of-bounds error? Why is it dangerous, and what are its potential consequences? 8. How do you calculate the number of elements in an array using the sizeof operator?
Provide the formula. What is a significant limitation of this method? 9. Why is it impossible to copy the contents of one array into another using the assignment
operator (arrayB = arrayA;)? What is the correct way to perform this operation? 10. Why does comparing two arrays using the equality operator (arrayA == arrayB) not check if their elements are equal? How should array comparison be done correctly? 11. What does the name of an array represent in terms of memory? 1. What is a pointer in C++ and what are its two main attributes? 2. Explain the difference between the & and * operators when working with pointers. 3. Why is pointer initialization critical and what dangers do uninitialized pointers pose? 4. What is the fundamental relationship between arrays and pointers in C++? 5. How does pointer arithmetic work and why does ptr + 1 advance by the size of the pointed type rather than 1 byte? 6. What is the difference between an array name and a pointer variable? Why can't you increment an array name? 7. What are the differences between const int*, int* const, and const int* const? 8. How can you safely iterate through an array using pointers, and what are the boundary risks? 9. What is a null pointer and why should you check for nullptr before dereferencing? 10. How do you access array elements using pointer syntax, and how does the compiler translate arr[i] internally? 1. What is a multidimensional array? How is a two-dimensional array structured in memory? 2. Explain the concept of an "array of arrays". How does this relate to the declaration int arr/ROWS//COLS;? 3. The name of a two-dimensional array without indices is a pointer constant. What does this pointer point to? What do the expressions *(A + i) and *(*(A + i) +j) mean for a two-dimensional array A? 4. Describe the different ways to access the element A/1/[2/ of a two-dimensional array
using pointers. 5. What is the rule for omitting the size of dimensions when initializing and when passing a multidimensional array to a function? Why is it allowed to omit only the first dimension? 6. Explain the principle of "row-major order" for storing two-dimensional arrays in memory.
How does this affect element access? 7. Why are nested loops the standard tool for processing multidimensional arrays?
Describe the typical pattern for iterating through a matrix. 1. How is a character string stored in memory in C++? What is the role of the null terminator (10), and why is it critical for C-style strings? 2. Why must the size of a char array declared to hold a string be at least one greater than the number of characters you intend to store? 3. The array name without an index is a pointer constant. What does the name of a char array point to? 4. What are the two main ways to initialize a C-style string? What is a common mistake when using the initializer list method, and what is its consequence? 5. Why is it necessary to add _CRT_SECURE_NO_WARNINGS to the preprocessor definitions in Visual Studio when working with many standard C library functions?
What is the alternative approach? 6. What is the key difference between stropy and strncpy? Why might strncpy be considered safer? 7. How does the stremp function determine if one string is "less than" another? Why can't you use the == operator to compare two C-style strings for content equality? 8. Describe the purpose and parameters of the strok function. How do you get all tokens from a string? 9. What do the functions strchr and strrchr do? How do they differ? 10. Explain what the strstr function returns and what it is commonly used for. 11. What is the purpose of the functions in the < cctype> header? Give three examples of such functions and their use. 12. What is the difference between tolower(c) and_tolower(c)? When should you use each? 1. What is a function in C++? Name the three core benefits of using functions in a program. 2. What is the difference between a function declaration (prototype) and a function definition? Provide examples. 3. What is a function signature? Which elements are part of the signature, and which are not? 4. What methods of passing parameters to a function do you know? Explain the difference between pass-by-value, pass-by-pointer, and pass-by-reference. 5. Why can't you pass an array to a function by value? What is the correct way to pass an array to a function? 6. What is variable scope? How is it related to functions? 7. How does a function return a value? What happens if a function with a non-void return type does not return a value on all control paths? 8. Can you use multiple return statements in a single function? Provide an example. 9. What is function overloading? What is it based on? 10. How is interaction between functions organized in a program? Provide an example program with several functions. 11. What are default parameters? How are they specified, and in what cases are they useful? 12. How can you prevent a function from modifying the data passed to it? What modifiers are used for this? 13. What is recursion? Provide an example of a recursive function. 14. What common errors occur when working with functions? How can they be avoided? 15. How do you use pointers to functions? Provide an example of declaring and calling a function through a pointer. 用中文回答
最新发布
11-18
整个编程用一个主函数搞定。32单片机,c语言编程,头文件名stm32f10x.h,oled是4口 首先,在开始的时候,OLED显示:“want to chat with me?”此时你可以选 择向单片机通过串口发送1或者2。1(好的,我想跟你聊呢)、2(对不起,我要忙着 学习呢)。 发送2后,LED灯开始呼吸,且呼吸速率逐渐加快,OLED显示“I am angry!!!”。这句话的下面显示time的倒计时,“time = x”,x从3逐渐变为 0。一秒钟减1,减为0时,LED灯熄灭,OLED重新显示“want to chat with me?”并且下面显示“you have x chances”,x初值是3,也就是说总共有3次机 会,每拒绝一次女友就会少一次机会,当chances的值减为0时,不再显示“want to chat with me?”和“you have x chances”,直接显示:“woohoo,let’s break up”(中文翻译为“呜呜呜,我们分手吧”),LED灯开始闪烁,每一秒钟 闪烁一次。5秒后程序重启。 发送1后,LED灯亮起,但是亮度为完全亮的一半亮度。OLED显示:“say please.....”此时可以向串口发送数字,通过数字来选择和女友聊天的内容。 发送1的话(聊天内容是天气如何),OLED显示:“what’s the weather like?”(估计显示不下,只要求必须显示关键词weather) 发送2的话(聊天内容是你喜欢我哪里),OLED显示:“what do you like about me?”(估计显示不下,只要求必须显示关键词like) 发送3的话(聊天内容是你曾经谈过几次恋爱),OLED显示:“how many times have you been in love?”(估计显示不下,只要求必须显示关键词in love ) 发送4的话(聊天内容是你有多爱我),OLED显示:“how many do you love me?”(估计显示不下,只要求必须显示关键词love me) 然后当你按下按键后,OLED下面显示倒计时“time = x”,x的值从9逐渐减 少,一直到0为止。在此过程中LED灯闪烁,并且闪烁速率逐渐加快。你必须在这10 秒内向串口发送你的答案, 如果没有time的值到0时,还没有发送完答案,那么LED灯将熄灭。并且OLED 显示:“woohoo,you do not love me”(中文翻译为“呜呜呜,你不爱我了”) 持续5秒OLED屏幕闪烁。然后,重新进入问问题阶段。但是LED灯的亮度明显变 暗。 如果及时发送了正确答案的话,那么LED灯将满亮。并且OLED显示:“I love you!!!”(此处不进行翻译)持续3秒OLED屏幕闪烁。然后,重新进入问问题阶 段。但是LED灯的亮度明显变亮。 LED灯的亮度有5个阶段,刚开始处于第3个阶段每一次成功答对问题,LED灯亮 度增加,第5阶段为满亮,第一阶段为熄灭。如果到了满亮或熄灭阶段,不必重新进 入问问题阶段。OLED屏幕闪烁完毕后,OLED显示:“let’s get married!!!”或 者“let’s break up now!!!”持续3秒后,程序重启。 (接下去,LED灯变成什么状态,就要执行相应的程序) 第一题的答案有:S(晴朗)、R(下雨)、W(多云)。回答任意一个即为正 确。 第二题的答案有:A(外貌)、S(灵魂)、L(都有)。只有回答L才是正确。 回答A或者SLED灯亮度减小一个阶段。 第三题的答案有:回答0是正确答案,1-3为半对答案,LED灯亮度减小一个阶 段,4以上LED灯直接熄灭。 第四题的答案有:回答10是正确答案,8或9为半对答案,LED灯亮度减小一个阶 段,6或7为半对答案,LED灯亮度减小两个阶段,6以下LED灯直接熄灭。 要求:在发完答案后,串口会把你的答案在屏幕上显示出来,格式为“your answer is xxx”  如果发送其他数字的话(选择除了1 2 3 4之外的题目),或者答案不在正确答案 范围里。说明你根本不了解你的女友,LED灯直接熄灭。             
06-01
32单片机编程,头文件名stm32f10x.h,c语言编程,oled是4口 首先,在开始的时候,OLED显示:“want to chat with me?”此时你可以选 择向单片机通过串口发送1或者2。1(好的,我想跟你聊呢)、2(对不起,我要忙着 学习呢)。 发送2后,LED灯开始呼吸,且呼吸速率逐渐加快,OLED显示“I am angry!!!”。这句话的下面显示time的倒计时,“time = x”,x从3逐渐变为 0。一秒钟减1,减为0时,LED灯熄灭,OLED重新显示“want to chat with me?”并且下面显示“you have x chances”,x初值是3,也就是说总共有3次机 会,每拒绝一次女友就会少一次机会,当chances的值减为0时,不再显示“want to chat with me?”和“you have x chances”,直接显示:“woohoo,let’s break up”(中文翻译为“呜呜呜,我们分手吧”),LED灯开始闪烁,每一秒钟 闪烁一次。5秒后程序重启。 发送1后,LED灯亮起,但是亮度为完全亮的一半亮度。OLED显示:“say please.....”此时可以向串口发送数字,通过数字来选择和女友聊天的内容。 发送1的话(聊天内容是天气如何),OLED显示:“what’s the weather like?”(估计显示不下,只要求必须显示关键词weather) 发送2的话(聊天内容是你喜欢我哪里),OLED显示:“what do you like about me?”(估计显示不下,只要求必须显示关键词like) 发送3的话(聊天内容是你曾经谈过几次恋爱),OLED显示:“how many times have you been in love?”(估计显示不下,只要求必须显示关键词in love ) 发送4的话(聊天内容是你有多爱我),OLED显示:“how many do you love me?”(估计显示不下,只要求必须显示关键词love me) 然后当你按下按键后,OLED下面显示倒计时“time = x”,x的值从9逐渐减 少,一直到0为止。在此过程中LED灯闪烁,并且闪烁速率逐渐加快。你必须在这10 秒内向串口发送你的答案, 如果没有time的值到0时,还没有发送完答案,那么LED灯将熄灭。并且OLED 显示:“woohoo,you do not love me”(中文翻译为“呜呜呜,你不爱我了”) 持续5秒OLED屏幕闪烁。然后,重新进入问问题阶段。但是LED灯的亮度明显变 暗。 如果及时发送了正确答案的话,那么LED灯将满亮。并且OLED显示:“I love you!!!”(此处不进行翻译)持续3秒OLED屏幕闪烁。然后,重新进入问问题阶 段。但是LED灯的亮度明显变亮。 LED灯的亮度有5个阶段,刚开始处于第3个阶段每一次成功答对问题,LED灯亮 度增加,第5阶段为满亮,第一阶段为熄灭。如果到了满亮或熄灭阶段,不必重新进 入问问题阶段。OLED屏幕闪烁完毕后,OLED显示:“let’s get married!!!”或 者“let’s break up now!!!”持续3秒后,程序重启。 (接下去,LED灯变成什么状态,就要执行相应的程序) 第一题的答案有:S(晴朗)、R(下雨)、W(多云)。回答任意一个即为正 确。 第二题的答案有:A(外貌)、S(灵魂)、L(都有)。只有回答L才是正确。 回答A或者SLED灯亮度减小一个阶段。 第三题的答案有:回答0是正确答案,1-3为半对答案,LED灯亮度减小一个阶 段,4以上LED灯直接熄灭。 第四题的答案有:回答10是正确答案,8或9为半对答案,LED灯亮度减小一个阶 段,6或7为半对答案,LED灯亮度减小两个阶段,6以下LED灯直接熄灭。 要求:在发完答案后,串口会把你的答案在屏幕上显示出来,格式为“your answer is xxx”  如果发送其他数字的话(选择除了1 2 3 4之外的题目),或者答案不在正确答案 范围里。说明你根本不了解你的女友,LED灯直接熄灭。             
06-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值