goto语句
本节阅读量:我们将讨论的下一种控制流语句是无条件跳转。无条件跳转会导致执行跳转到代码中的另一个位置。术语“无条件”意味着跳转总是发生(不像if语句或switch语句,跳转仅根据表达式的结果有条件地发生)。
在C++中,无条件跳转是通过goto语句实现的,跳转到的位置是通过使用语句标签来标识的。下面是goto语句和语句标签的示例:
|
|
在此程序中,要求用户输入一个非负数。如果输入负数,程序将使用goto语句跳回tryAgain标签。然后再次要求用户输入新数字。这样,我们可以不断地要求用户输入,直到输入有效的内容。
下面是该程序的示例运行:
|
|
标签语句具有函数作用域
在前面,我们讨论了两种作用域:局部(代码块)作用域和文件(全局)作用域。标签语句使用第三种作用域:函数作用域,这意味着标签在整个函数中都是可见的,甚至在其声明点之前也是可见的。goto语句及其对应的标签语句必须出现在同一函数中。
虽然上面的示例显示了向后跳转(到函数中的前一点)的goto语句,但goto语句也可以向前跳转:
|
|
这会打印:
|
|
除了向前跳转外,上面的程序中还有几个值得一提的细节。
首先,注意标签语句必须与某条语句相关联(标签必须标记一条语句)。因为函数末尾没有语句,所以这里必须使用空语句。其次,由于标签语句具有函数作用域,即使end还没有声明,也能够跳到标有end的语句,不需要对标签语句进行前向声明。第三,值得明确指出的是,上面的程序风格很差——用if语句跳过print语句会比用goto语句更好。
goto有两个主要限制:只能在单个函数内跳转(不能从一个函数跳到另一个函数),并且如果向前跳转,不能跳过变量的显式初始化。例如:
|
|
请注意,可以向后跳转;当再次执行后续的初始化语句时,变量会被重新初始化。
避免使用goto
在C++(以及其他现代高级语言)中,应避免使用goto。著名计算机科学家 Dijkstra 在一篇著名但难读的论文《Go To Statement Considered Harmful》中阐述了避免使用goto的理由。goto的主要问题是它允许程序员在代码中随意跳转。这会产生一种不太好听地被称为“意大利面条代码”的东西。意大利面条代码的执行路径就像一碗意大利面条一样纠结扭曲,使得代码逻辑极难阅读。
正如Dijkstra有点幽默地说的那样,“程序员的质量是他们编写的程序中go-to语句密度的反比函数”。
几乎所有使用goto语句编写的代码,都可以用C++中的其他控制流(如if语句和循环)更清楚地表达。一个值得注意的例外是,当您需要退出嵌套循环但不想退出整个函数时,使用goto跳转到循环之后的位置,可能是最简洁的解决方案。
最佳实践
避免goto语句(除非替代方案对代码可读性的影响明显)。