55

学C的时候,老师整天跟我说:“不要用goto,那是坏习惯,丑陋,危险!” 等等。

那么,为什么一些内核程序员会使用goto,例如在这个函数中,它可以用一个简单的替换

while(condition) {} 

或者

do {} while(condition);

我无法理解。while在某些情况下使用 goto 而不是/ do-会更好while吗?如果是这样,为什么?

4

3 回答 3

61

历史背景:我们应该记住,Dijkstra 在 1968 年写了Goto 被认为是有害的,当时许多程序员将其用作结构化编程(、、等) goto的替代品。ifwhilefor

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

案例分析:

示例代码如下所示:

    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;

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

于 2012-10-21T19:26:47.383 回答
31

在这个例子中,我怀疑它是关于将 SMP 支持改进到最初以非 SMP 安全方式编写的代码中。与重构函数相比,添加goto again;路径要简单得多,侵入性也更小。

我不能说我很喜欢这种风格,但我也认为goto出于意识形态原因避免这种风格是被误导的。一种特殊的goto使用情况(与此示例不同)是 wheregoto仅用于在函数中向前移动,从不向后移动。这类用法永远不会导致由goto.

于 2012-10-21T19:02:50.773 回答
1

非常好的问题,我认为只有作者才能提供明确的答案。正如@Izkata 所解释的那样,我将通过说它可以开始使用它进行错误处理来添加我的一点猜测,然后大门也可以将其用于基本循环。

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

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

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

如果你只做一个简单的循环,你就不会越线,所以它仍然是可读和可维护的,主要是风格问题。也就是说,因为它们可以很容易地使用语言提供的循环关键字来完成,正如您在问题中所指出的那样,我的建议仍然是避免使用gotofor 循环,仅仅是因为for, do/whileorwhile构造在设计上更加优雅。

于 2019-06-26T11:05:54.837 回答