while(1) ..用break代替goto

发布于 2024-07-25 19:19:56 字数 732 浏览 9 评论 0原文

我在 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 来编写此代码:

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 没有回答我的问题。

I found the following code in a C program:

while (1)
{
    do_something();
    if (was_an_error()) break;

     do_something_else();
     if (was_an_error()) break;

     [...]

     break;
}
[cleanup code]

Here while(1) is used as local emulation of "finally". You can also write this using gotos:

do_something()
if (was_an_error()) goto out;

do_something_else()
if (was_an_error()) goto out;

[...]
out:
[cleanup code]

I thought the goto solution is a usual idiom. I have seen several occurrences of this idiom in the kernel sources and it is also mentioned in Diomidis Spinellis' "Code Reading" book.

My question is: What solution is better? Is there any specific reason to use the while(1) solution?

Question 943826 doesn't answer my question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

若言繁花未落 2024-08-01 19:19:57

尽管通常不鼓励使用 goto,但在某些罕见的情况下,例如您的情况,最佳实践并不是最好的。

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

Though the use of goto is discouraged usually, some rare situations like yours is a place where best-practices are not the best.

So, if goto makes the clearest code I would use it. using a while(true) loop to emulate goto is something unnatural. What you really need is a goto!

南…巷孤猫 2024-08-01 19:19:57

为什么不使用一系列 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 可能会被禁止,但 break 通常也是如此continue 因此循环不一定是解决方法。

Why not use a series of if statements? I usually write it this way, as I find it much clearer than a loop:

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]

Also if you are working to strict coding standards, yes goto is likely to be forbidden, but often so are break and continue so the loop is not necessarily a workaround for that.

生生不灭 2024-08-01 19:19:57

“break”理解块作用域的语义,而“goto”则不了解它。 换句话说,“while-break”可以翻译成函数式语言,比如带尾递归的 Lisp,而“goto”则不能。

"break" understands the semantics of the block scope, while "goto" is oblivious to it. In other words, "while-break" can be translated into functional languages like Lisp with tail-recursion, "goto" cannot.

初雪 2024-08-01 19:19:57

通常,GOTO 被认为是不好的,但在某些仅通过 GOTO 向前跳转的地方,它们并没有那么糟糕。 人们像躲避瘟疫一样避开 GOTO,但恕我直言,深思熟虑的使用 GOTO 有时是更好的解决方案。

Normally, GOTOs are considered bad but at some places where there are only Forward Jumps through GOTOs, they are not AS bad. People avoid GOTO like plague but a well-thought-out use of GOTO is sometimes a better solution IMHO.

尐籹人 2024-08-01 19:19:57

“do while”和“goto out”在以下方面有所不同:

1.局部变量初始化

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) 使用相同的宏处理不同级别的退出。 代码如上所示。 goto ... 宏则不然,因为不同级别需要不同的标签。

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

"do while" and "goto out" are different on these area:

1.local variable initialization

void foo(bool t = false)
{
    if (t)
    {
        goto DONE;
    }

    int a = 10; // error : Goto bypass local variable's initialization 

    cout << "a=" << a << "\n";
DONE:
}

It is fine to initialize in-place local variables in do ... while(0) block.

void bar(bool t = false)
{
    do{
        if (t)
        {
            break; 
        }

        int a = 10;  // fine

        cout << "a=" << a << "\n";
    } while (0);

}

2 difference for Macros. "do while" is a slight better. "goto DONE" in a Macro is so not the case.
If the exit code is more complicated, let see like this:

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

and you write this code again and again, you will probably put them into a Macro.

#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.  \
}

And the code become:

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) handles different levels of exiting with same macro. Code is shown above. goto ... is not the case for Macro cause you need different labels for different levels.

By saying that, I do not like both of them. I'd prefer to use the exception method. If exception is not allowed, then I use "do ... while(0)", since the whole block is indented, it is actually easier to read than "goto DONE" style.

我是有多爱你 2024-08-01 19:19:57

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

I think that this use (for resource management) of goto is ok.

多彩岁月 2024-08-01 19:19:57

任何原因而无法使用 goto,请使用它

  • 项目约定中的
  • 如果您由于 lint 工具禁止的

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

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

Use it if you can't use goto for whatever reason

  • forbidden in your project's conventions
  • forbidden by your lint tool

I also think that is also one of the cases where macros aren't evil:

#define DO_ONCE for (int _once_dummy = 0; _once_dummy < 1; _once_dummy++)
血之狂魔 2024-08-01 19:19:57

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

I like the while(1) approach. I use it myself. Especially, when the loop might get repeated by continue, e.g. when an element is processed inside such loop, and it's done in multiple approaches.

冰魂雪魄 2024-08-01 19:19:57

切勿使用条件永久为真的条件循环。 既然条件总是为真,为什么还要使用条件循环呢?

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

Never use a condition loop with a permanently true condition. Since the condition is always true, why use a conditional loop?

Permanently true conditions are most directly represented by a goto.

梦境 2024-08-01 19:19:57

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

While using "goto" for error handling situations is fairly common, I'd still prefer the "while" solution (or "do while"). In the "goto" case, there are far fewer things that the compiler can guarantee. If you make a typo in the label name, the compiler can't help you there. If someone uses another goto to another label in that block, there's a good chance the cleanup code won't get called. When you use the more structured flow control constructs you are always guaranteed which code will run once the loop is over.

苍景流年 2024-08-01 19:19:56

对 GOTO 看似普遍的反感很大程度上是由于 Edsger Dijkstra 的信“Go To 声明被认为是有害的”。

如果您决定不使用 goto,类似的东西

do {
    ...
while(0);

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

与 goto 相比,(ab) 使用 do/break/while 或 while/break 的一个优点是保证您不会跳转到构造之上 - goto 可用于跳转到同一函数中较早的标签。

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

在我看来,由于普遍讨厌 GOTO 而盲目地避免使用它,比根据具体情况仔细推理出使用它的情况更具破坏性。 我使用的经验法则是“Linux 内核会这样做吗?如果是的话,它不会那么糟糕”。 用现代软件工程的任何其他优秀示例替代 Linux 内核。

The seemingly universal revultion to GOTO is largely due to Edsger Dijkstra's letter "Go To Statement Considered Harmful".

If you are determined not to use goto, something like

do {
    ...
while(0);

is probably safer than while(1) { ... } since it guarantees that you will not inadvertently loop (and if you are inadvertently looping, with while(1) you are probably inadvertently looping infinitely).

The one advantage that (ab)using do/break/while or while/break for this purpose has over goto is that you are guaranteed not to be jumping above the construct -- goto can be used to jump to a label earlier within the same function.

The disadvantage that do/break/while etc. have over goto is that you are limited to one exit point (immediately after the loop). In some cases you might need a staged cleanup: e.g., when you open a file handle, malloc some memory, read from the file... if the read fails, you need to clean up the malloc. If the malloc fails, you don't need to clean it up, but you still need to clean up the file handle. With goto, you can have one label per stage of cleanup and jump to precisely the right point depending on where your error occurred.

In my opinion blindly avoiding GOTO because of the prevalent hatred of it is more damaging than carefully reasoning out a case for its use on a case-by-case basis. A rule of thumb I use is "does the Linux kernel do it? If so, it can't be that bad". Substitute linux kernel with any other good example of modern software engineering.

枯寂 2024-08-01 19:19:56

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

Putting the code into a separate function, and using return to exit early is another way to do it, with the benefit of easy integration of a return code indicating the nature of the failure.

倾听心声的旋律 2024-08-01 19:19:56

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

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

// Cleanup code

I know my style isn't the coolest possible, but I prefer it because it doesn't need any special constructs and is concise and not too hard to understand:

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

// Cleanup code
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文