做{…} while(false)

我正在看一个人的一些代码,注意到他似乎有一个function模式:

<return-type> function(<params>) { <initialization> do { <main code for function> } while(false); <tidy-up & return> } 

这不 ,更奇特(实际的代码是相当整洁,不出所料)。 这不是我以前见过的,我想知道是否有人可以想到背后的逻辑 – 用不同语言的背景吗?

do{...}while(false)你可以break do{...}while(false)

许多人指出,它经常被用作编写“goto”的尴尬手段。 如果直接写入函数,这可能是真的。

在macros观上,OTOH {做某事; } while(false)是macros调用之后强制分号的一种方便的方式,绝对不允许其他标记跟随。

另外一种可能是,那里有一个循环,或者预计将来会迭代迭代(例如,在testing驱动的开发中,不需要迭代来通过testing,但是逻辑上,如果循环的话是有意义的该function需要比目前要求的更普遍)

rest时间可能是答案,但我会提出一个其他的想法。

也许他想要一个本地定义的variables,并使用这个构造来获得一个新的范围。

请记住,尽pipe最近的C ++允许在任何地方使用{...} ,但情况并非总是如此。

我已经看到,当函数有很多潜在的退出点时,它被用作一个有用的模式,但是无论函数如何退出,总是需要相同的清理代码。

如果/ else-if树更容易阅读,通过只要到达一个退出点就中断,其余的逻辑内联后,就可以使一个令人厌烦的if / else-if树更容易阅读。

这种模式在没有goto语句的语言中也很有用。 也许这就是原始程序员学习模式的地方。

我已经看过这样的代码,所以你可以使用break作为goto的种类。

这只是一个颠倒,而不使用goto这个词来获得goto tidy-up歌词。

这是不好的forms,因为当你在外面使用其他循环, while breaks变得模糊的读者。 “这是否应该退出?或者这只是打破内部循环?

这个技巧被程序员使用,他们害怕在代码中使用明确的goto 。 上面的代码的作者希望能够直接从代码中间跳到“清理和返回”点。 但他们不想使用标签和明确的goto 。 相反,他们可以在上面的“假”周期内使用一个break来达到同样的效果。

这是一个非常普遍的做法。 在C。 我试着想,就好像你想以一种“我不使用goto ”的方式对自己说谎 。 考虑一下,用goto也不会有什么问题。 实际上它也会降低缩进级别。

尽pipe如此,我注意到,经常这样做,而循环趋于增长。 然后他们得到if s和else内部,渲染代码实际上不是很可读,更不用说可testing。

那些做..通常打算做一个清理 。 通过一切手段,我宁愿使用RAII,并从一个简短的function提前返回 。 另一方面, C并没有像C ++那样为您提供更多的便利,而是做了一个do ..而最好的方法之一是做清理。

我认为写下break而不是goto end比较方便。 你甚至不需要想出一个标签的名称,使意图更清晰:你不想跳到一个具有特定名称的标签。 你想离开这里。

无论如何,你也可能需要大括号。 所以这是do{...}while(false); 版:

 do { // code if (condition) break; // or continue // more code } while(false); 

如果你想使用goto ,这就是你必须expression的方式:

 { // code if (condition) goto end; // more code } end: 

我认为第一个版本的含义更容易理解。 另外它更容易编写,更容易扩展,更容易翻译成不支持goto的语言等。


最常被提及的关于使用break担心是这是一个严重伪装的goto 。 但实际上, breakreturn有更多的相似之处:两条指令都跳出了一段代码,这个代码与goto相比是非常结构化的。 尽pipe如此,两个指令都允许在一段代码中有多个退出点,这有时会造成混淆。 毕竟,我会尽力寻求最明确的解决scheme,无论是在具体的情况下。

它看起来像一个C程序员。 在C ++中,自动variables具有用于清理的析构函数,所以在返回之前不应该有任何需要整理的东西。 在C中,你没有这个RAII语言,所以如果你有共同的清理代码,你可以goto它,或者像上面一样使用一个循环。

与C ++习惯用法相比,它的主要缺点是,如果抛出exception,它将不会整理。 C没有例外,所以这不是一个问题,但它确实使它成为C ++中的一个坏习惯。

也许它被使用,以便可以在里面使用break来在任何点放弃进一步代码的执行:

 do { if (!condition1) break; some_code; if (!condition2) break; some_further_code; // … } while(false); 

我认为这是为了使用breakcontinue语句。 某种“goto”代码逻辑。

很简单:显然你可以随时用break语句跳出假循环。 而且, do块是一个单独的范围(也可以用{ ... }来实现)。

在这种情况下,使用RAII(对象自动在函数结束时自动破坏)可能会更好。 另一个类似的构造是使用goto – 是的,我知道这是邪恶的 ,但它可以用来具有共同的清理代码,如下所示:

 <return-type> function(<params>) { <initialization> <main code for function using "goto error;" if something goes wrong> <tidy-up in success case & return> error: <commmon tidy-up actions for error case & return error code or throw exception> } 

(旁白:在Lua中使用do-while-false构造提出失踪的continue语句。)

许多回答者给出了do{(...)break;}while(false) 。 我想通过另一个现实生活中的例子来补充图片。

在下面的代码中,我必须根据data指针指向的地址设置枚举器operation 。 因为一个switch-case只能用在标量types上,所以我这样做效率不高

 if (data == &array[o1]) operation = O1; else if (data == &array[o2]) operation = O2; else if (data == &array[on]) operation = ON; Log("operation:",operation); 

但是,由于Log()和其余的代码重复了任何选定的操作值,所以我徘徊在地址已经被发现的时候如何跳过剩余的比较。 这是do{(...)break;}while(false)派上用场的地方。

 do { if (data == &array[o1]) { operation = O1; break; } if (data == &array[o2]) { operation = O2; break; } if (data == &array[on]) { operation = ON; break; } } while (false); Log("operation:",operation); 

有人可能会奇怪,为什么他不能在if语句中用break来做同样的事情,比如:

 if (data == &array[o1]) { operation = O1; break; } else if (...) 

break只与最接近的封闭循环或开关交互,无论是forwhile还是do .. whiletypes,所以不幸的是,这是行不通的。

作者多大了?

我问,因为我曾经遇到过一些实时的Fortran代码,那是在80年代后期。 事实certificate,这是一个非常好的方法来模拟没有它们的操作系统上的线程。 你只需把整个程序(你的调度程序)放到一个循环中,然后逐个调用你的“线程”例程,线程例程本身就是循环,直到发生多种情况之一(通常是一定数量的时间已经过去了),它是“协作式多任务”,因为它是由各个线程放弃CPU,所以其他人不会饿死,可以嵌套循环子程序调用来模拟线程优先级带。

除了已经提到的“goto例子”之外,do … while(0)成语在macros定义中有时被用来提供定义中的括号,并且编译器仍然可以在末尾添加一个分号一个macros观调用。

http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/52f670f1292f30a4?tvc=2&q=while+(0);

几个解释。 第一个是通用的,第二个是具有参数的C预处理器macros:

stream量控制

我已经看到这用纯C代码。 基本上,这是一个更安全的转换版本,因为你可以打破它,所有的内存得到适当的清理。

为什么会像goto好? 那么,如果你有代码,几乎每一行都可以返回一个错误,但是你需要以同样的方式对所有的代码作出反应(例如,在清理之后把错误传给你的调用者),通常它更具可读性,以避免if( error ) { /* cleanup and error string generation and return here */ }因为它避免了重复的清理代码。

但是,在C ++中,你正是为了这个目的而使用了exception+ RAII,所以我认为它是不好的编码风格。

分号检查

如果在函数式的macros调用之后忘记了分号,则参数可能以不希望的方式收缩并编译成有效的语法。 想象一下macros

 #define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo"); 

这被无意称为

 if( foo ) PRINT_IF_DEBUGMODE_ON("Hullo\n") else doSomethingElse(); 

“else”将被认为是与gDebugModeOn相关联的,所以当foofalse ,准确的反向意思就会发生。

为临时variables提供一个范围。

由于do / while有大括号,所以临时variables有一个明确的范围,他们不能逃脱。

避免“可能不需要的分号”警告

一些macros只能在debugging版本中激活。 你将它们定义为:

 #if DEBUG #define DBG_PRINT_NUM(n) printf("%d\n",n); #else #define DBG_PRINT_NUM(n) #endif 

现在,如果你在一个条件版本中使用它,它将编译成

 if( foo ) ; 

许多编译器将此视为与

 if( foo ); 

这是经常写意外。 所以你得到一个警告。 do {} while(false)隐藏了这个来自编译器的内容,并被它接受为表示你真的不想在这里做任何事情。

避免通过条件来捕获行

上例中的macros:

 if( foo ) DBG_PRINT_NUM(42) doSomething(); 

现在,在一个debugging版本中,由于我们还习惯性地包含了分号,所以编译得很好。 然而,在发布版本中突然变成:

 if( foo ) doSomething(); 

或者更清晰的格式化

 if( foo ) doSomething(); 

这根本不是什么意图。 在macros周围添加do {…} while(false)会将丢失的分号变成编译错误。

OP的含义是什么?

一般来说,你想在C ++中使用exception来进行error handling,并且使用模板而不是macros。 但是,在极less数情况下,您仍然需要macros(例如,使用令牌粘贴生成类名称时)或限制为纯C,这是一个有用的模式。

我同意大多数海报关于这个用法的一个伪装的goto。 macros也被认为是编写代码的潜在动机。

我也看到这种构造在混合C / C ++环境中用作穷人的例外。 如果在循环中遇到通常会遇到exception的情况,可以使用带有“break”的“do {} while(false)”跳过代码块的末尾。

我也曾经在商店中使用这种构造,在那里实行“单function返回”的意识形态。 再次,这是代替一个明确的“goto” – 但动机是避免多个返回点,而不是“跳过”代码,并继续在该函数内的实际执行。

我使用Adobe InDesign SDK工作,InDesign SDK示例几乎具有这样的function。 这是因为这个function通常很长。 在哪里需要执行QueryInterface(…)以从应用程序对象模型中获取任何内容。 所以通常每个QueryInterface都跟着if不行,就打破。

很多人已经说过这个构build和goto之间的相似性,并且expression了对goto的偏好。 也许这个人的背景包括了编码准则严格禁止goto的环境?

我能想到的另一个原因是它装饰大括号,而我相信在一个新的C + +标准裸括号不好(ISO C不喜欢他们)。 否则,静音像lint一样的静态分析器。

不知道为什么你会想要它们,也许是可变范围,或debugging器的优势。

请参阅Trivial Do while循环 ,并从括号中select大括号 。

澄清我的术语(我相信遵循标准用法):

赤裸的括号

 init(); ... { c = NULL; mkwidget(&c); finishwidget(&c); } shutdown(); 

空括号(NOP):

 {} 

例如

 while (1) {} /* Do nothing, endless loop */ 

 if (finished) { closewindows(&windows); freememory(&cache); } 

这将成为

 if (finished) closewindows(&windows); freememory(&cache); 

如果花括号被删除,从而改变执行的stream程,而不仅仅局部variables的范围。 因此不是“独立”或“裸体”。

裸括号或块可以用来表示任何代码段,这些代码段可能是您希望标记的(内联)function的可能性,但不是当时的重构。

这是一个仿效GOTO做法,因为这两者实际上是相同的:

 // NOTE: This is discouraged! do { if (someCondition) break; // some code be here } while (false); // more code be here 

和:

 // NOTE: This is discouraged, too! if (someCondition) goto marker; // some code be here marker: // more code be here 

另一方面,这些都应该用s来完成:

 if (!someCondition) { // some code be here } // more code be here 

虽然嵌套可以得到一个难看的,如果你只是把一个长的前向GOTO的string变成嵌套的if s。 真正的答案是适当的重构,但不是模仿古老的语言结构。

如果你拼命地在GOTOjoin一个algorithm,你可以用这个成语来做。 这当然是非标准的,也是一个很好的指标,你不会严格遵守语言的预期成语。

实际上,我不知道任何类似C的语言在哪里/哪个地方是一个习惯性的解决scheme。

你也许可以把整个混乱重构成更明智的东西,使其更具地道性,更具可读性。

这很有趣。 正如其他人所说的那样,循环内部可能会被打断。 我会这样做:

 while(true) { <main code for function> break; // at the end. }