C 或 C++ 中好的 goto 示例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/245742/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Examples of good gotos in C or C++
提问by fizzer
In this thread, we look at examples of good uses of goto
in C or C++. It's inspired by an answerwhich people voted up because they thought I was joking.
在本主题中,我们将查看goto
在 C 或 C++中良好使用的示例。它的灵感来自人们投票支持的答案,因为他们认为我在开玩笑。
Summary (label changed from original to make intent even clearer):
总结(标签从原来的改变使意图更加清晰):
infinite_loop:
// code goes here
goto infinite_loop;
Why it's better than the alternatives:
为什么它比替代品更好:
- It's specific.
goto
is the language construct which causes an unconditional branch. Alternatives depend on using structures supporting conditional branches, with a degenerate always-true condition. - The label documents the intent without extra comments.
- The reader doesn't have to scan the
intervening code for early
break
s (although it's still possible for an unprincipled hacker to simulatecontinue
with an earlygoto
).
- 这是具体的。
goto
是导致无条件分支的语言结构。替代方案取决于使用支持条件分支的结构,并具有退化的始终为真条件。 - 标签记录意图,没有额外的注释。
- 读者不必扫描早期
break
s的中间代码(尽管无原则的黑客仍然可以continue
使用 early进行模拟goto
)。
Rules:
规则:
- Pretend that the gotophobes didn't win. It's understood that the above can't be used in real code because it goes against established idiom.
- Assume that we have all heard of 'Goto considered harmful' and know that goto can be used to write spaghetti code.
- If you disagree with an example, criticize it on technical merit alone ('Because people don't like goto' is not a technical reason).
- 假装恐高症没有赢。据了解,上述内容不能在实际代码中使用,因为它违反了既定的习惯用法。
- 假设我们都听说过“Goto 被认为有害”,并且知道 goto 可用于编写意大利面条式代码。
- 如果您不同意某个示例,请仅根据技术价值对其进行批评(“因为人们不喜欢 goto”不是技术原因)。
Let's see if we can talk about this like grown ups.
让我们看看我们是否可以像大人一样谈论这个。
Edit
编辑
This question seems finished now. It generated some high quality answers. Thanks to everyone,
especially those who took my little loop example seriously. Most skeptics were concerned
by the lack of block scope. As @quinmars pointed out in a comment, you can always put braces around the
loop body. I note in passing that for(;;)
and while(true)
don't give you the braces
for free either (and omitting them can cause vexing bugs). Anyway, I won't waste any more
of your brain power on this trifle - I can live with the harmless and idiomatic for(;;)
and while(true)
(just as well if I want to keep my job).
这个问题现在似乎结束了。它产生了一些高质量的答案。感谢所有人,尤其是那些认真对待我的小循环示例的人。大多数怀疑论者担心缺少块范围。正如@quinmars 在评论中指出的那样,您始终可以在循环体周围放置大括号。我顺便指出for(;;)
,并while(true)
没有给你免费任括号(并忽略他们可能会导致错误伤脑筋)。无论如何,我不会在这件小事上浪费你的脑力——我可以忍受那些无害的和惯用的for(;;)
和while(true)
(如果我想保住我的工作也一样)。
Considering the other responses, I see that many people view goto
as something you always
have to rewrite in another way. Of course you can avoid a goto
by introducing a loop,
an extra flag, a stack of nested if
s, or whatever, but why not consider whether goto
is
perhaps the best tool for the job? Put another way, how much ugliness are people prepared to endure to avoid using a built-in language feature for its intended purpose? My take is that
even adding a flag is too high a price to pay. I like my variables to represent things in
the problem or solution domains. 'Solely to avoid a goto
' doesn't cut it.
考虑到其他回复,我看到很多人认为goto
你总是必须以另一种方式重写。当然,你可以goto
通过引入一个循环、一个额外的标志、一堆嵌套的if
s 或其他任何东西来避免 a ,但为什么不考虑是否goto
可能是这项工作的最佳工具呢?换句话说,为了避免将内置语言功能用于其预期目的,人们准备忍受多少丑陋?我的看法是,即使添加一个标志也太昂贵了。我喜欢我的变量来表示问题或解决方案域中的事物。'goto
仅仅为了避免' 并不能削减它。
I'll accept the first answer which gave the C pattern for branching to a cleanup block. IMO, this makes the strongest case for a goto
of all the posted answers, certainly
if you measure it by the contortions a hater has to go through to avoid it.
我将接受第一个答案,该答案给出了分支到清理块的 C 模式。IMO,这是goto
所有已发布答案中最有力的案例,当然,如果您通过仇恨者为避免它而必须经历的扭曲来衡量它。
回答by Greg Rogers
Heres one trick I've heard of people using. I've never seen it in the wild though. And it only applies to C because C++ has RAII to do this more idiomatically.
这是我听说人们使用的一种技巧。不过我从来没有在野外见过它。它只适用于 C,因为 C++ 有 RAII 可以更惯用地做到这一点。
void foo()
{
if (!doA())
goto exit;
if (!doB())
goto cleanupA;
if (!doC())
goto cleanupB;
/* everything has succeeded */
return;
cleanupB:
undoB();
cleanupA:
undoA();
exit:
return;
}
回答by Paul Nathan
The classic need for GOTO in C is as follows
C中GOTO的经典需求如下
for ...
for ...
if(breakout_condition)
goto final;
final:
There is no straightforward way to break out of nested loops without a goto.
没有 goto 就没有直接的方法可以跳出嵌套循环。
回答by fizzer
Here's my non-silly example, (from Stevens APITUE) for Unix system calls which may be interrupted by a signal.
这是我的非愚蠢示例,(来自 Stevens APITUE)用于可能被信号中断的 Unix 系统调用。
restart:
if (system_call() == -1) {
if (errno == EINTR) goto restart;
// handle real errors
}
The alternative is a degenerate loop. This version reads like English "if the system call was interrupted by a signal, restart it".
另一种方法是退化循环。这个版本读起来像英语“如果系统调用被信号中断,则重新启动它”。
回答by Mitch Wheat
If Duff's device doesn't need a goto, then neither should you! ;)
如果 Duff 的设备不需要 goto,那么您也不应该!;)
void dsend(int count) {
int n;
if (!count) return;
n = (count + 7) / 8;
switch (count % 8) {
case 0: do { puts("case 0");
case 7: puts("case 7");
case 6: puts("case 6");
case 5: puts("case 5");
case 4: puts("case 4");
case 3: puts("case 3");
case 2: puts("case 2");
case 1: puts("case 1");
} while (--n > 0);
}
}
code above from Wikipedia entry.
以上代码来自维基百科条目。
回答by zvrba
回答by ephemient
Very common.
很常见。
do_stuff(thingy) {
lock(thingy);
foo;
if (foo failed) {
status = -EFOO;
goto OUT;
}
bar;
if (bar failed) {
status = -EBAR;
goto OUT;
}
do_stuff_to(thingy);
OUT:
unlock(thingy);
return status;
}
The only case I ever use goto
is for jumping forwards, usually out of blocks, and never into blocks. This avoids abuse of do{}while(0)
and other constructs which increase nesting, while still maintaining readable, structured code.
我使用的唯一情况goto
是向前跳跃,通常是从块中跳出来,从不进入块中。这避免了do{}while(0)
增加嵌套的滥用和其他构造,同时仍然保持可读的结构化代码。
回答by Brian R. Bondy
I have nothing against gotos in general, but I can think of several reasons why you wouldn't want to use them for a loop like you mentioned:
我一般不反对 goto,但我可以想到几个原因,为什么你不想像你提到的那样将它们用于循环:
- It does not limit scope hence any temp variables you use inside won't be freed until later.
- It does not limit scope hence it could lead to bugs.
- It does not limit scope hence you cannot re-use the same variable names later in future code in the same scope.
- It does not limit scope hence you have the chance of skipping over a variable declaration.
- People are not accustomed to it and it will make your code harder to read.
- Nested loops of this type can lead to spaghetti code, normals loops will not lead to spaghetti code.
- 它不限制范围,因此您在内部使用的任何临时变量直到稍后才会被释放。
- 它不限制范围,因此可能会导致错误。
- 它不限制范围,因此您不能在以后的代码中在同一范围内重复使用相同的变量名称。
- 它不限制范围,因此您有机会跳过变量声明。
- 人们不习惯它,它会使您的代码更难阅读。
- 这种类型的嵌套循环可能导致意大利面条式代码,法线循环不会导致意大利面条式代码。
回答by Mitch Wheat
@fizzer.myopenid.com: your posted code snippet is equivalent to the following:
@fizzer.myopenid.com:您发布的代码片段相当于以下内容:
while (system_call() == -1)
{
if (errno != EINTR)
{
// handle real errors
break;
}
}
I definitely prefer this form.
我绝对喜欢这种形式。
回答by Adam Liss
One good place to use a goto is in a procedure that can abort at several points, each of which requires various levels of cleanup. Gotophobes can always replace the gotos with structured code and a series of tests, but I think this is more straightforward because it eliminates excessive indentation:
使用 goto 的一个好地方是可以在多个点中止的过程,每个点都需要不同级别的清理。Gotophobes 总是可以用结构化代码和一系列测试来代替 goto,但我认为这更直接,因为它消除了过多的缩进:
if (!openDataFile()) goto quit; if (!getDataFromFile()) goto closeFileAndQuit; if (!allocateSomeResources) goto freeResourcesAndQuit; // Do more work here.... freeResourcesAndQuit: // free resources closeFileAndQuit: // close file quit: // quit!
回答by JaredPar
Even though I've grown to hate this pattern over time, it's in-grained into COM programming.
尽管随着时间的推移我越来越讨厌这种模式,但它已经深入到 COM 编程中。
#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
HRESULT hr = S_OK;
IfFailGo( pFoo->PerformAction() );
IfFailGo( pFoo->SomeOtherAction() );
Error:
return hr;
}