在功能结束时跳转到清理时,GOTO被认为是无害的吗?

Phi*_*lip 20 c coding-style goto

goto在几个SO讨论中已经对这个声明进行了长时间的讨论(见这个那个),我当然不想重振那些激烈的辩论.

相反,我想专注于gotos 的单个用例并讨论它的价值和可能的替代方案.

请考虑以下代码片段,这在(至少我自己的)FSM中很常见:

while (state = next_state()) {
        switch (state) {
                case foo:
                        /* handle foo, and finally: */
                        if (error) goto cleanup;
                        break;
                case bar:
                        /* handle bar, and finally: */
                        if (error) goto cleanup;
                        break;
                /* ...other cases... */
        }
}

return ok;

cleanup:
/* do some cleanup, i.e. free() local heap requests, adjust global state, and then: */
return error;
Run Code Online (Sandbox Code Playgroud)

在一个单独的函数中交换清理东西只是为了保存gotos似乎很尴尬.另一方面,我们已经被提出来谴责goto尽可能使用s.

我的问题:我的代码示例被认为是好风格吗?
如果没有,是否有可行的替代方案?

请坚持上述具体用法goto.我不想深入研究关于一般用途的另一个讨论goto.

pmg*_*pmg 11

你的用法goto还可以.它没有打破使用goto的两种好方法.

  1. goto必须在源头中下载(几行)
  2. 最内层的块goto labels必须包含goto语句

  • 你会自动谴责在一个函数中"向上"的"goto"吗?我有几种情况,一个罕见的角落案例必须从顶部重新启动一个复杂的功能,并添加一个标签和goto比重组整个功能,因为循环本来就不那么突兀了.当然我本来可以让函数调用本身,但我通常认为递归(并依赖于编译器的尾调用优化来删除它)比`goto`更糟糕的进攻. (3认同)
  • 我不会自动谴责任何违反"规则"的内容,而且我不会自动接受任何符合"规则"的内容:)但是,当存在错误并且没有指示在哪里找到时,代码就会违反首先仔细审查规则. (3认同)

Jör*_*ann 6

我不会将清理逻辑提取到自己的函数中并从不同的地方调用它,而是考虑将switch语句提取到一个单独的函数中并从中返回错误代码.在while循环中,您可以检查返回代码并进行清理并在必要时返回.

如果你有开关和清理逻辑之间共享了很多资源,那么我想转到将preferrable周围路过这一切的状态.

  • örn:这是一个有趣的解决方案,非常感谢您指出这一点.它遇到了与单独清理功能相同的问题,因为它需要传递(可能很多)某些"本地"资源,但对于简单的设置,这似乎是要走的路. (2认同)

Ben*_*igt 6

你有时不需要转到switch.使用两者switchgoto只是增加了复杂性.

while (state) {
        switch (state) {
                case cleanup:
                        /* do some cleanup, i.e. free() local heap requests, adjust global state, and then: */
                        return error;
                case foo:
                        /* handle foo, and finally: */
                        if (error) { state = cleanup; continue; }
                        break;
                case bar:
                        /* handle bar, and finally: */
                        if (error) { state = cleanup; continue; }
                        break;
                /* ...other cases... */
        }
        state = next_state();
}

return ok;
Run Code Online (Sandbox Code Playgroud)

  • 将清理放在状态机中是一个很好的改进。除此之外,我很确定有科学研究证明 continue == goto 具有另一种风格。你的代码所做的是向上跳而不是向下跳,继续而不是转到,这根本不是改进,它等于意大利面条。只是教条地说 goto 不好,因为它被命名为 goto 并没有帮助你的代码。 (2认同)

mde*_*dec 5

我在 OpenBSD 内核中见过 goto 以这种方式使用,特别是在 ATA 设备驱动程序中(一个这样的例子),我个人认为这是一种很好的风格,因为它有助于准确地说明正在发生的情况以及代码如何与相应的匹配有限状态机。当尝试根据规范验证 FSM 的功能时,使用 goto 会在一定程度上提高清晰度。