女孩和机器人一起思考

一起动脑筋 · 先看一个小故事

机器人读到函数里又出现同一个函数名。它不是回到旧任务擦掉重写,而是开始一个新的小任务。

把过程摊开来看

沿着编号看一遍,再用自己的话讲一遍
  1. 第一次处理 n = 2
  2. 第二次处理 n = 1
  3. 第三次处理 n = 0
  4. 返回依次完成等待的任务

函数可以在执行中调用自己,每次调用有独立的参数和执行位置。

用三张写着 n = 2、1、0 的任务卡叠起来,就能看见这些调用同时处在不同阶段。

01函数真的可以调用自己?

可以。看一个最简单的例子:

void countdown(int n) {
    if (n == 0) {
        return;
    }

    std::cout << n << '\n';
    countdown(n - 1);
}

countdown(3) 执行时,它会继续调用 countdown(2),然后是 countdown(1)

02这是不是同一个函数“重新开始”?

不是。

每一次调用都会产生一次新的函数调用状态。它们执行的是同一份函数代码,但参数值和局部变量可以不同。

第一次调用

n = 3

第二次调用

n = 2

第三次调用

n = 1

03为什么每次调用不会把前一次的 n 覆盖掉?

因为每个活跃的函数调用都有自己的参数和局部状态。

countdown(3) 还没有结束时,countdown(2) 会作为新的调用开始;前一层的 n = 3 仍属于前一层调用。

04自调用为什么不能无限继续?

如果函数一直调用自己,又没有办法停止,活跃调用会越来越多。

void bad() {
    bad();
}

这种函数没有终止路径,在实际程序中通常最终会因为资源耗尽而失败。

05自调用和循环一样吗?

循环

控制流回到同一个循环结构继续下一轮。

递归调用

创建新的函数调用,等更深层完成后再返回。

两者都可以表达重复过程,但运行模型并不相同。

06为什么要先学调用栈再深入递归?

因为递归最容易误解的地方不是代码,而是多层调用同时存在

只有看清“谁调用了谁、现在在哪一层、返回到哪里”,递归才不会变成死记模板。

你已经知道了什么

  • 函数可以调用自己。
  • 每一次自调用都是一次新的函数调用。
  • 不同调用可以拥有不同参数和局部状态。
  • 自调用不是“原地重新开始”,也不是复制函数代码。
  • 没有停止机制的递归会不断增加调用层数。

下一篇:什么是递归?

轮到你来试一试

最内层 n 变成 0,外层那张 n = 2 的卡会被改成 0 吗?

想好了吗?点开看解释

不会。按值传入的参数属于各自的调用,外层仍记着自己的 2。