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

dme*_*ter 17 c goto

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

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

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

     [...]

     break;
}
[cleanup code]
Run Code Online (Sandbox Code Playgroud)

这里while(1)用作"终于"的本地模拟.你也可以用gotos 写这个:

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

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

[...]
out:
[cleanup code]
Run Code Online (Sandbox Code Playgroud)

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

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

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

jmt*_*mtd 33

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

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

do {
    ...
while(0);
Run Code Online (Sandbox Code Playgroud)

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

即(AB)使用做的一个好处/休息/ while或同时/为此,打破了在后藤是,你保证不会是跳跃上面的构造- goto语句可以用来在同一较早跳转到一个标签功能.

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

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

  • 我已经看到使用`do {... break ...} while(0);`技巧的代码,我可以证明它在不给你任何东西的同时妨碍了可读性.如果你正在做一个'goto` - 并且你在这里做了_are_那么 - 至少要明确它.对于错误处理/清理目的,C代码中的`goto`没有任何问题. (5认同)
  • 实际上它不仅是Linux内核,来自MS的Windows驱动程序样本全部使用goto,Solaris unix内核驱动程序也使用它,BSD内核代码等...底线goto被广泛使用和广泛使用,人们知道他们在做什么. (2认同)

Bar*_*lly 10

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


Mak*_*kis 8

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

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

// Cleanup code


Ara*_*raK 7

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

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

  • +1使用GOTO在嵌入式系统中很常见,以便在发生故障后进行清理. (2认同)
  • +1"仅仅因为"不鼓励使用goto.不鼓励避免意大利面条代码并提高清晰度.真正的规则是功能应该清晰简单,因此使用更难以掌握goto的替代方法并不是IMO的一个好选择.任何涉及一次性循环的东西都属于这一类. (2认同)

fin*_*nnw 5

为什么不使用一系列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]
Run Code Online (Sandbox Code Playgroud)

此外,如果您正在努力达到严格的编码标准,goto可能会禁止使用yes ,但通常也是如此break,continue因此循环不一定是解决方法.


yog*_*man 5

"break"理解块范围的语义,而"goto"则忽略了它.换句话说,"while-break"可以被翻译成具有尾递归的Lisp等函数式语言,"goto"则不能.


Jac*_* Wu 2

“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:
}
Run Code Online (Sandbox Code Playgroud)

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

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

        int a = 10;  // fine

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

当你一次又一次地编写这些代码时,你可能会将它们放入宏中。

#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.  \
}
Run Code Online (Sandbox Code Playgroud)

代码变成:

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);
Run Code Online (Sandbox Code Playgroud)

3.do...while(0) 使用同一宏处理不同级别的退出。代码如上所示。goto ... 宏则不然,因为不同级别需要不同的标签。

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