c-为什么有些内核程序员使用goto而不是简单的while循环?

当我学习C时,老师整天告诉我:“不要使用goto,这是一个坏习惯,很丑陋,很危险!” 等等。

为什么然后使用某些内核程序员在此函数中使用while,例如可以用一个简单的函数替换它

while(condition) {} 

要么

do {} while(condition);

我不明白 在某些情况下,使用goto代替while / do-while更好吗? 如果是这样,为什么?

musicmatze asked 2020-01-22T16:01:16Z
3个解决方案
58 votes

历史背景:我们应该记得Dijkstra在1968年写了Goto被认为有害,当时许多程序员使用goto来替代结构化编程(gotoSETUPCOMPUTE SOME VALUES等)。

这是44年后的今天,很少在野外找到goto的这种用法。 很久以前,结构化编程已经赢了。

案例分析:

示例代码如下所示:

    SETUP...
again:
    COMPUTE SOME VALUES...
    if (cmpxchg64(ptr, old_val, val) != old_val)
        goto again;

结构化版本如下所示:

SETUP...
do {
    COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);

当我查看结构化版本时,我立即想到“这是一个循环”。 当我查看goto版本时,我认为它是一条直线,结尾处带有“重试”情况。

goto版本在同一列上同时具有SETUPCOMPUTE SOME VALUES,这强调了大多数时候,控制流都通过这两者。 结构化版本将SETUPCOMPUTE SOME VALUES放在不同的列上,强调控制可能会不同地通过它们。

这里的问题是您想在代码中强调什么? 您可以将其与goto进行错误处理:

结构化版本:

if (do_something() != ERR) {
    if (do_something2() != ERR) {
        if (do_something3() != ERR) {
            if (do_something4() != ERR) {
                ...

转到版本:

if (do_something() == ERR)  // Straight line
    goto error;             // |
if (do_something2() == ERR) // |
    goto error;             // |
if (do_something3() == ERR) // |
    goto error;             // V
if (do_something4() == ERR) // emphasizes normal control flow
    goto error;

生成的代码基本相同,因此我们可以将其视为印刷问题,例如缩进。

Dietrich Epp answered 2020-01-22T16:02:48Z
29 votes

在此示例的情况下,我怀疑这是将SMP支持改写为最初以非SMP安全的方式编写的代码。 与重新构造功能相比,添加goto路径要简单得多且侵入性较小。

我不能说我很喜欢这种风格,但是出于意识形态的原因,我也认为避免使用goto是不正确的。 goto用法的一种特殊情况(与本示例不同)是goto仅在功能中用于向前移动而从不向后移动。 此类用法永远不会导致goto产生循环构造,并且它几乎始终是实现所需行为的最简单,最清晰的方法(通常是清理并返回错误)。

R.. answered 2020-01-22T16:01:43Z
0 votes

很好的问题,我想只有作者才能提供明确的答案。 我会猜测一下,它可以开始使用它来进行错误处理,正如@Izkata所解释的,然后大门也开放了用于基本循环的机会。

我认为错误处理的用法在系统编程中是合法的。 函数会随着进程的进行逐渐分配内存,如果遇到错误,它将goto适当的标签从该点以相反的顺序释放资源。

因此,如果错误在第一次分配后发生,它将跳到最后一个错误标签,以仅释放一个资源。 同样,如果错误在最后一次分配之后发生,它将跳到第一个错误标签并从那里运行,释放所有资源,直到函数结束。 仍然需要谨慎使用这种错误处理模式,特别是在修改代码时,强烈建议使用valgrind和单元测试。 但是可以说,它比其他方法更具可读性和可维护性。

使用goto的一条黄金法则是避免使用所谓的意大利面条式代码。 尝试在每个for语句及其各自的标签之间画线。 如果您有划线,那么您已经划线了:)。 do/while的这种用法很难阅读,并且是难以跟踪错误的常见来源,因为它们可以在依赖BASIC之类的语言进行流控制的语言中找到。

如果仅执行一个简单的循环,就不会产生交叉线,因此它仍然可读且可维护,很大程度上取决于样式。 就是说,因为就像您在问题中所指出的那样,使用提供的语言提供的循环关键字就可以轻松地完成它们,所以我的建议仍然是避免将goto用于循环,仅是因为fordo/whilewhile的构造更多 设计典雅。

Nagev answered 2020-01-22T16:03:29Z
translate from https://stackoverflow.com:/questions/13001067/why-do-some-kernel-programmers-use-goto-instead-of-simple-while-loops