而(1)..打破而不是转到

我在C程序中找到了以下代码:

while (1) { do_something(); if (was_an_error()) break; do_something_else(); if (was_an_error()) break; [...] break; } [cleanup code] 

这里while(1)用作“finally”的本地仿真。 你也可以用goto s写这个:

 do_something() if (was_an_error()) goto out; do_something_else() if (was_an_error()) goto out; [...] out: [cleanup code] 

我认为goto解决方案是一种常用的习惯用法。 我已经在内核源代码中看到过几次这种习惯用法,在Diomidis Spinellis的“代码阅读”一书中也提到了这一点。

我的问题是:什么解决方案更好? 有没有具体的理由使用while(1)解决方案?

问题943826没有回答我的问题。

对GOTO的看似普遍的反驳很大程度上归功于Edsger Dijkstra的信“Go To Statement Considered Harmful”。

如果你决定不使用goto,那就像

 do { ... while(0); 

可能比while(1){…}更安全,因为它保证你不会无意中循环(如果你无意中循环,而while(1)你可能无意中无限循环)。

(ab)为此目的使用do / break / while或while / break的一个优点就是保证不会跳过构造 – goto可以用来早先跳转到标签function。

do / break / while等过度goto的缺点是你被限制在一个出口点(紧接在循环之后)。 在某些情况下,您可能需要进行分阶段清理:例如,当您打开文件句柄时,malloc一些内存,从文件中读取…如果读取失败,则需要清理malloc。 如果malloc失败,您不需要清理它,但仍需要清理文件句柄。 使用goto,每个清理阶段可以有一个标签,并根据错误发生的位置跳转到正确的点。

在我看来,由于对它的普遍仇恨而盲目地避免使用GOTO比在每个案例中仔细推理其使用案例更具破坏性。 我使用的经验法则是“Linux内核是否做到了?如果是这样,它就不会那么糟糕”。 用任何其他现代软件工程的好例子替换linux内核。

将代码放入一个单独的函数,并使用return提前退出是另一种方法,可以轻松集成返回代码,指示失败的性质。

虽然通常不鼓励使用goto,但像你这样的一些罕见的情况是最佳实践不是最好的地方。

所以,如果goto使用最清晰的代码,我会使用它。 使用while(true)循环来模拟goto是不自然的。 你真正需要的是一个转到!

我知道我的风格不是最酷的,但我更喜欢它,因为它不需要任何特殊的结构,简洁而且不太难理解:

 error =(!error)&& do_something1();
 error =(!error)&& do_something2();
 error =(!error)&& do_something3();

 //清理代码

为什么不使用一系列if语句? 我通常以这种方式编写它,因为我发现它比循环更清晰:

 bool ok = true; do_something(); if (was_an_error()) ok = false; if (ok) { do_something_else(); if (was_an_error()) ok = false; } if (ok) { do_something_else_again(); if (was_an_error()) ok = false; } [...] [Cleanup code] 

此外,如果您正在努力达到严格的编码标准,那么很可能禁止使用goto ,但通常是breakcontinue因此循环不一定是解决方法。

“break”理解块范围的语义,而“goto”则忽略了它。 换句话说,“while-break”可以翻译成像Lisp这样的函数式语言,尾部递归,“goto”不能。

通常情况下,GOTO被认为是坏的,但在某些只有通过GOTO的前向跳跃的地方,它们并不差。 人们避免GOTO像瘟疫一样,但经过深思熟虑的GOTO使用有时是更好的解决方案恕我直言。

我认为goto的这种用途(用于资源管理)是可以的。

如果您因任何原因无法使用goto,请使用它

  • 禁止在您的项目的约定
  • 你的棉绒工具禁止

我也认为这也是宏不是邪恶的情况之一:

 #define DO_ONCE for (int _once_dummy = 0; _once_dummy < 1; _once_dummy++) 

我喜欢while(1)方法。 我自己用它。 特别是,当循环可能通过继续重复时,例如当在这样的循环内处理元素时,并且它在多个方法中完成。

切勿使用具有永久真实条件的条件循环。 由于条件始终为真,为什么要使用条件循环?

永久真实条件最直接由goto表示。

虽然使用“goto”进行error handling的情况相当普遍,但我仍然更喜欢“while”解决方案(或“do while”)。 在“goto”的情况下,编译器可以保证的东西要少得多。 如果您在标签名称中输入拼写错误,则编译器无法帮助您。 如果有人使用另一个goto到该块中的另一个标签,则很可能不会调用清理代码。 当您使用更结构化的流控制结构时,您始终可以确保在循环结束后将运行哪些代码。

“do while”和“goto out”在这些区域有所不同:

1.local变量初始化

 void foo(bool t = false) { if (t) { goto DONE; } int a = 10; // error : Goto bypass local variable's initialization cout << "a=" << a << "\n"; DONE: } 

可以在do ... while(0)块中初始化就地局部变量。

 void bar(bool t = false) { do{ if (t) { break; } int a = 10; // fine cout << "a=" << a << "\n"; } while (0); } 

2宏差异。 “do while”稍微好一些。 宏中的“goto DONE”并非如此。 如果退出代码更复杂,请看这样:

 err = some_func(...); if (err) { register_err(err, __LINE__, __FUNC__); #if defined (_DEBUG) do_some_debug(err) #endif break; } 

并且你反复编写这段代码,你可能会将它们放入宏中。

 #define QUIT_IF(err) \ if (err) \ { \ register_err(err, __LINE__, __FUNC__); \ DO_SOME_DEBUG(err) \ break; // awful to put break in macro, but even worse to put "goto DONE" in macro. \ } 

代码变成:

 do { initial(); do { err = do_step1(); QUIT_IF(err); err = do_step2(); QUIT_IF(err); err = do_step3(); QUIT_IF(err); .... } while (0); if (err) { // harder for "goto DONE" to get here while still using macro. err = do_something_else(); } QUIT_IF(err); ..... } while (0); 

3.do ... while(0)使用相同的宏处理不同的退出级别。 代码如上所示。 转到...不是宏的情况,因为您需要不同级别的不同标签。

通过这样说,我不喜欢他们两个。 我更喜欢使用exception方法。 如果不允许exception,那么我使用“do ... while(0)”,因为整个块是缩进的,它实际上比“goto DONE”样式更容易阅读。