我正在看一些个人的代码,并注意到他的功能似乎有一个模式:
<return-type> function(<params>)
{
<initialization>
do
{
<main code for function>
}
while(false);
<tidy-up & return>
}
Run Code Online (Sandbox Code Playgroud)
它不错,更奇特(实际代码相当简洁且不足为奇).这不是我以前见过的东西,我想知道是否有人能想到它背后的任何逻辑 - 也许是用不同语言的背景?
Tho*_*ing 169
你可以break出局do{...}while(false).
Ben*_*igt 119
很多人都指出,它经常与休息一起用作编写"goto"的尴尬方式.如果它直接写在函数中,那可能就是这样.
在宏中,OTOH do { something; } while (false)是在宏调用之后强制分号的一种便捷方式,绝对不允许其他令牌跟随.
另一种可能性是,曾经有一个循环或预计将来会添加迭代(例如,在测试驱动开发中,不需要迭代来通过测试,但从逻辑上讲,如果在那里循环则有意义该功能需要比目前要求的更为通用)
Hog*_*gan 24
作为goto的休息可能是答案,但我会提出另一个想法.
也许他想要一个本地定义的变量并使用这个结构来获得一个新的范围.
请记住,虽然最近的C++适用于{...}任何地方,但情况并非总是如此.
Cam*_*ron 20
我已经看到它在函数有许多潜在的退出点时用作有用的模式,但无论函数如何退出,总是需要相同的清理代码.
它可以让烦人的if/else-if树更容易阅读,只要在到达出口点时必须中断,其余的逻辑后续内联.
此模式在没有goto语句的语言中也很有用.也许这就是原始程序员学习模式的地方.
Joh*_*ler 10
这只是while为了在goto tidy-up不使用goto这个词的情况下得到sematics的变态.
它的坏的形式,因为当你使用其他外内循环while的breaks变得模糊给读者. "这应该转到退出吗?或者这只是为了打破内循环?"
Rob*_*ert 10
我认为写break代替更方便goto end.您甚至不必为标签想出一个名称,使意图更清晰:您不希望跳转到具有特定名称的标签.你想离开这里.
也有可能你需要大括号.所以这是do{...}while(false);版本:
do {
// code
if (condition) break; // or continue
// more code
} while(false);
Run Code Online (Sandbox Code Playgroud)
如果您想使用,这就是您必须表达的方式goto:
{
// code
if (condition) goto end;
// more code
}
end:
Run Code Online (Sandbox Code Playgroud)
我认为第一个版本的含义更容易掌握.此外,它更容易编写,更容易扩展,更容易翻译成不支持的语言goto等.
关于使用的最常提到的担忧break是它被严重伪装goto.但实际上break有更多的相似之处return:两个指令都跳出了一个代码块,与之相比,它的结构非常相似goto.然而,两个指令都允许代码块中的多个退出点,这有时会令人困惑.毕竟我会尝试寻求最明确的解决方案,无论具体情况如何.
这个技巧被程序员使用goto,他们太害羞而不能在代码中使用显式.上面代码的作者希望能够从代码中间直接跳转到"清理并返回"点.但他们不想使用标签和明确的goto.相反,他们可以使用break上述"假"循环体内部来达到同样的效果.
这是一种非常普遍的做法.在Ç.我试着把它想象成你想以某种方式欺骗自己 "我没有使用goto".考虑一下,goto使用类似的东西也没有错.实际上它也会降低缩进程度.
尽管如此,我注意到,这种do..while循环往往会增长.然后他们得到ifs和elses内部,使代码实际上不是很可读,更不用说可测试了.
这些do..while通常是为了清理.无论如何,我宁愿使用RAII并从短期函数中提前返回.另一方面,C并没有像C++那样为您提供那么多便利,这是进行清理的最佳方法之一.do..while
几种解释。第一个是通用的,第二个是特定于带有参数的 C 预处理器宏:
我已经看到在普通 C 代码中使用了它。基本上,它是一个更安全的 goto 版本,因为您可以摆脱它并且所有内存都得到正确清理。
为什么goto-like 会是好的?好吧,如果您的代码几乎每一行都可以返回错误,但是您需要以相同的方式对所有代码做出反应(例如,在清理后将错误交给您的调用者),则通常更易读,以避免出现if( error ) { /* cleanup and error string generation and return here */ }as它避免了重复的清理代码。
但是,在 C++ 中,出于这个目的,您有例外 + RAII,所以我认为它是糟糕的编码风格。
如果您在类似函数的宏调用后忘记了分号,则参数可能会以不希望的方式收缩并编译为有效的语法。想象一下宏
#define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo");
Run Code Online (Sandbox Code Playgroud)
这不小心被称为
if( foo )
PRINT_IF_DEBUGMODE_ON("Hullo\n")
else
doSomethingElse();
Run Code Online (Sandbox Code Playgroud)
“else”将被视为与 相关联gDebugModeOn,因此当foo是 时false,将发生与预期完全相反的情况。
由于 do/while 有花括号,临时变量有一个明确定义的范围,它们无法逃脱。
某些宏仅在调试版本中激活。您将它们定义为:
#if DEBUG
#define DBG_PRINT_NUM(n) printf("%d\n",n);
#else
#define DBG_PRINT_NUM(n)
#endif
Run Code Online (Sandbox Code Playgroud)
现在,如果您在条件中的发布版本中使用它,它会编译为
if( foo )
;
Run Code Online (Sandbox Code Playgroud)
许多编译器认为这与
if( foo );
Run Code Online (Sandbox Code Playgroud)
这通常是不小心写的。所以你会收到警告。do{}while(false) 向编译器隐藏了这一点,并被它接受作为您真的不想在这里做任何事情的指示。{}
上一个示例中的宏:
if( foo )
DBG_PRINT_NUM(42)
doSomething();
Run Code Online (Sandbox Code Playgroud)
现在,在调试版本中,由于我们还习惯性地包含分号,因此编译得很好。但是,在发布版本中,这突然变成了:
if( foo )
doSomething();
Run Code Online (Sandbox Code Playgroud)
或更清晰的格式
if( foo )
doSomething();
Run Code Online (Sandbox Code Playgroud)
这根本不是我们想要的。在宏周围添加 do{ ... }while(false) 会将缺少的分号变成编译错误。
通常,您希望在 C++ 中使用异常进行错误处理,并使用模板而不是宏。但是,在非常罕见的情况下,您仍然需要宏(例如,使用标记粘贴生成类名时)或仅限于纯 C,这是一种有用的模式。
| 归档时间: |
|
| 查看次数: |
29754 次 |
| 最近记录: |