🔷 第五章 循环语句
📄 一句话概括

goto 标签; —— 无条件跳转:跳到代码里某个带标签的位置,想跳哪跳哪。实例06 用 goto + if 手搓了一个循环(能算出 55,语法完全成立),但它把"跳来跳去"当成了循环结构。结论很干脆:认得它、别用它——结构化三件套(顺序/分支/循环)就是为取代它而生的。

📝 本节和前面几节的关系

5.2.2 的 for(;;) 变体在说"循环怎么写得紧凑",本篇说的是反例:用 goto 手工拼循环,写出来跟 for 一模一样的效果,却丢了"一眼看出结构"的好处。它和 break/continue 也完全不同:后两者是结构化的跳出(跳去哪里语言规定死),goto 的落点由你随便标。

一、实例 06:用 goto 实现循环

#include <iostream>
using namespace std;

int main() {
    int i = 1, sum = 0;

loop:                          // 标签:goto 的落点
    sum += i;
    i++;
    if (i <= 10) goto loop;    // 还没数到 10,跳回标签

    cout << "1 到 10 的累加结果是 " << sum << endl;
    return 0;
}

看到什么算成功:输出 55,和实例01/03 结果一样——这正说明 goto 能替代循环,也正说明没必要:同样 4 行逻辑,用 for 写成 for (int i=1; i<=10; i++) sum += i;,读者一眼看出"从头数到 10 的循环";goto 版得靠人脑拼 loop: 和 goto loop; 的连线。

语法要点:标签就是一个标识符 + 冒号(loop:),可以起名、可以多个;goto 只能跳同一个函数里的标签,不能跨函数。

二、为什么"人见人嫌":三条硬伤

硬伤 说明
结构不可预测 编译器不报错,但读代码的人在 goto 处不知道接下来去哪;流程图会画成一团乱线("面条代码")
容易绕过初始化 跳转落点之前若有必须的准备(变量初始化、资源打开),一跳就全跳过,行为诡异
难调试难维护 断点、单步执行的"顺着走"假设失效;改一处跳转,牵连一片

历史注脚:1968 年 Dijkstra 那篇著名的信《Goto 语句有害》推动了结构化编程——顺序、分支、循环三种结构足够表达一切逻辑,且保持"可读"。所以 goto 在教学书里只作为"了解"出现。

极少数合法场景(知道有这回事即可,本课不用):从多重嵌套的最内层一次性跳到函数尾部做统一清理。稳妥做法是多用函数拆分 + return,或标志变量逐层退。

三、常见误区

  1. "goto 能跨越函数"——不能,只能跳同一函数内的标签;跨函数是 return/函数调用的事。
  2. "标签可以写在任何语句前"——语法上可以,但跳到变量声明之后再用该变量是未定义行为;goto 与作用域天生不友好。
  3. "goto 更快/更灵活"——现代编译器对结构化代码的优化比手搓跳转好;灵活换来的是可读性崩盘,不划算。
自测三问(点开对答案) 1. `goto 标签;` 的执行效果?——无条件跳到同函数内该标签处,中间语句全部跳过。 2. 实例06 为什么说"没必要"?——同样逻辑 for/while 写得更短、结构一眼可见。 3. goto 能跳去另一个函数吗?——不能,只能在同一个函数内部跳。

相关笔记