跳转到内容

C++ 编程

来自维基教科书,开放书籍,开放世界

goto 关键字不鼓励使用,因为它使得难以追踪程序逻辑,从而导致错误。goto 语句导致当前执行线程跳转到指定的标签。

语法
label:
  statement(s);

goto label;

在一些罕见情况下,goto 语句允许编写整洁的代码,例如,在处理多个退出点导致函数退出时的清理代码时(并且异常处理或对象析构函数不是更好的选择)。除了这些罕见情况,无条件跳转的使用通常是复杂设计的征兆,因为存在多层嵌套语句。

在例外情况下,例如在进行大量优化时,程序员可能需要对代码行为有更多控制;goto 允许程序员指定执行流直接且无条件地跳转到所需的标签。标签 是在函数中其他地方给标签语句起的名称。

注意
软件工程领域有一篇由 W. A. Wulf 撰写的经典论文,名为 "反对 GOTO 的论据",于 1972 年 10 月在第 25 届 ACM 全国会议上发表,当时关于 goto 语句的争论达到了顶峰。在这篇论文中,Wulf 认为 goto 语句应该被视为危险。Wulf 还以他对效率的评论而闻名:“在效率的名义下(不一定能真正实现效率)所犯的编程错误比任何其他原因都要多——包括盲目的愚蠢。”

例如,goto 可以用来跳出两个嵌套循环。这个例子在将第一个遇到的非零元素替换为零后跳出循环。

for (int i = 0; i < 30; ++i) {
  for (int j = 0; j < 30; ++j) {
    if (a[i][j] != 0) {
       a[i][j] = 0;
       goto done;
     }
  }
}
done:
/* rest of program */

虽然简单,但它们很快就会导致代码难以阅读和维护。

// snarled mess of gotos

int i = 0;
  goto test_it;
body:
  a[i++] = 0;
test_it:
  if (a[i]) 
    goto body;
/* rest of program */

比等效的

for (int i = 0; a[i]; ++i) {
  a[i] = 0;
}
/* rest of program */

Goto 通常用于性能至关重要的函数,或在机器生成的代码(例如由 yacc 生成的解析器)的输出中。

goto 语句几乎应该始终避免使用,但有一些罕见的情况可以提高代码的可读性。其中一种情况是“错误部分”。

示例

#include <new>
#include <iostream>

...

int *my_allocated_1 = NULL;
char *my_allocated_2 = NULL, *my_allocated_3 = NULL;
my_allocated_1 = new (std::nothrow) int[500];

if (my_allocated_1 == NULL)
{  
  std::cerr << "error in allocated_1" << std::endl;
  goto error;
}

my_allocated_2 = new (std::nothrow) char[1000];

if (my_allocated_2 == NULL)
{  
  std::cerr << "error in allocated_2" << std::endl;
  goto error;
}
    
my_allocated_3 = new (std::nothrow) char[1000];

if (my_allocated_3 == NULL)
{  
  std::cerr << "error in allocated_3" <<std::endl;
  goto error;
}
return 0;
    
error:
  delete [] my_allocated_1;
  delete [] my_allocated_2;
  delete [] my_allocated_3;
  return 1;

这种结构避免了处理错误的来源,并且比使用控制结构的等效结构更简洁。因此,它不易出错。

注意
虽然上面的例子展示了 goto 的合理使用,但在实际中并不常见。异常 以更清晰、更有效和更组织的方式处理此类情况。这将在“异常处理”中详细讨论。使用 RAII 来管理资源(例如内存)也可以避免对上面所示的大部分显式清理代码的需求。

华夏公益教科书