跳转至

为何应避免使用 GOTO 语句?

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/why-should-goto-be-avoided

53336 文章评分:

3.1

为何应避免使用 GOTO 语句?

匿名作者 2016年4月14日 星期四

IDL 的 GOTO 语句是一种控制语句,用于跳转到代码中的特定位置。它与 C++ 及许多其他语言中的 "goto" 类似。你可能会说:"多方便啊!如果满足某些条件,就能快速跳转到目标代码段。"

别急。

大多数程序员会告诉你应该避免使用 GOTO 语句。实际上,IDL 的官方文档也不建议使用它。准确地说,文档并非只是建议避免使用,而是直接声明使用它是一种糟糕的编程实践:

"GOTO 语句通常被认为是一种会导致程序结构混乱的糟糕编程实践。应避免使用。"

为何应避免使用 GOTO 语句?

既然我们知道了 GOTO 可能并非什么好东西,现在来看看几个应该避免使用它的原因。当然,这并非一份详尽的清单。

令人困惑的代码

当代码随意跳转时,很快就会变得难以理解。同一个例程中 GOTO 越多,情况就越糟糕。即使是顺序执行的代码,调试已经足够具有挑战性。当你在尝试单步调试时,代码却四处跳转,调试就会变成一场噩梦。

无限循环

如果 goto 的目标位置在 goto 调用语句的上方,很容易陷入无限循环。如果代码中没有可靠的退出机制(例如条件语句中的 RETURN 语句),这几乎是必然发生的。

在 catch 块中使用 GOTO 的危险

catch 块 中放置 goto 语句可能很诱人。如果发生错误,捕获它,然后跳转到执行某些恢复操作的代码段,对吧?

这样做的问题在于,如果你没有取消 catch,并且在 goto 目标点之后稍后的代码中又发生了错误,它会被 catch 语句重新捕获,然后你又陷入了无限循环的问题(提示:在执行恢复操作之前,请务必取消 catch)。然而,如果你取消了 catch,那么你就取消了主线代码的错误处理程序,然后你又跳回到主线代码中。后续的错误将不会被处理。除非你的例程中其他地方有另一个 catch 块,或者在更高的作用域层级有一个。当这种情况发生时,祝你好运能调试成功(参见上面的"令人困惑的代码"部分)。

一个 catch 块通常应该执行恢复操作,然后从例程中返回,而不是试图跳转到例程中的其他地方。

GOTO 的替代方案

除了上面的例子,还有一些场景下使用 GOTO 听起来可能是最好或最简单的做法。好消息是,在编程中,几乎总是有多种方法可以实现同一件事,所以很可能有使用 GOTO 的替代方案。

嵌套循环

BREAK 语句是立即退出循环的简单方法。但遗憾的是,它只退出当前循环,在多个循环嵌套的情况下没有帮助。下面展示了使用和不使用 goto 的情况。请注意第二个示例包含一个特殊标志,仅用于指示需要从每个循环中跳出。

更好的解决方案: 为什么不把循环代码移到它自己的子例程中呢?你可能需要传递一些参数,比如循环的内容,但这会使代码更简洁。现在,要退出循环,只需从子例程返回即可。

自动垃圾回收

GOTO 的一个常见用途是执行"公共退出",换句话说,在从例程返回之前总是调用某个代码段。也许这样做是为了重置变量或关闭在例程中打开的任何文件单元。也可能是在从例程返回之前做一些其他类型的清理工作。

有一种方案不仅能确保退出后执行公共清理,还能保证清理总会执行,那就是创建一个"作用域内"对象,由 IDL 的自动垃圾回收机制进行清理。

基本思路是,将任何清理代码放在临时对象的 ::Cleanup 方法中。当退出例程时,对象会离开其作用域,一旦对象离开作用域,IDL 会自动销毁或"垃圾回收"该对象。在此过程中,Cleanup 方法会被自动调用。我去年写了一篇关于作用域内对象的博客,你可以在这里找到:here

有适合使用 GOTO 的好时机吗?

随着时间的推移,我学到了关于编程规则的一点——它们就是用来被打破的,但当你打破它们时,请谨慎行事。

偶尔,使用 goto 语句确实是最简单的做法,例如,如果你想快速跳过某些代码或执行多路分支。有时你只是想用"不使用 goto"的经验法则来换取便利,这可以是个人偏好的问题。确实,与其他能提供同等功能的其他逻辑结构相比,使用 goto 通常需要编写的代码行数更少。

归根结底,尽管 goto 几乎总是可以被其他逻辑形式替代,但它今天仍然存在并且是合法的 IDL 语法。使用它确实存在不少隐患,但如果谨慎并少量使用,它也可以成为一个有用的工具。

用于传感器网络规划的可视域分析 使用 Jagwire 分发您的空间数据