这个递归函数如何在C ++中运行?

时间:2013-12-10 04:20:42

标签: c++ recursion output

我想知道如何生成以下代码示例中的第二个输出。在if语句和递归函数调用结束后,函数如何能够向后计数?

void Recursion(int x)
{
    if(x < 4) {
        cout << x << " "; // 1st output: 1 2 3
        Recursion(x + 1); 
    }

    cout << x << " "; // 2nd output: 4 3 2 1
}

int main()
{
    Recursion(1);
}

6 个答案:

答案 0 :(得分:3)

我要将这个问题解释为“这个函数怎么能记住它返回时它正在做什么?”。

C ++标准没有说明(IIRC,它留给实现找到有效的解决方案)。但实际上,答案是“堆栈”。

在计算机科学中,堆栈通常是先进先出的数据结构。将序列1,2和3推入堆栈,然后执行三次弹出,然后得到3,2,然后是1。

在早期,编程语言通常不支持递归或重入调用。每个函数/过程都拥有一小块内存,它存储了它的参数,局部变量以及完成后返回的函数。如果你试图调用一个已经运行的函数,那就意味着在空间中存储两组局部变量和两个返回地址,这是一个错误。

然而,IIRC的一项创新是Algol编程语言支持递归。大约在同一时间,“处理器堆栈”正在成为一种东西。

处理器堆栈(以及其他内容)允许您使用不同的方法来处理参数,局部变量和返回地址。每个函数不需要永久分配的块 - 在调用函数时分配一个块(在堆栈的顶部)。当前“堆栈帧”的位置是相对于当前“堆栈指针”的。这意味着您可以同时在堆栈上为同一个函数设置多个堆栈帧。

因此调用函数涉及在堆栈顶部创建一个新的堆栈帧,并调整堆栈指针以适应。从函数返回涉及丢弃该堆栈帧并向后调整堆栈指针,因此顶部堆栈帧现在是调用者的堆栈帧。该调用者可能是也可能不是同一个函数 - 这并不重要,因为每个调用都有自己的堆栈帧,存储一组独立的参数,局部变量,一个单独的返回地址等。

所以就在Recursion (3)调用之前,堆栈看起来像......

|-------------------+-------------------+
| Recursion Frame 1 | Recursion Frame 2 |
|---------------+---+---------------+---+
| ???           | X | ???           | X |
|---------------+---+---------------+---+
| ???           | 1 | ???           | 2 |
|---------------+---+---------------+---+
                                        ^
                                        |
                                      STACK
                                     POINTER

???代表“看家”这样的东西,比如寄回地址。

答案 1 :(得分:1)

想想第一次调用Recursion。

该函数将打印一个“1”,然后其他东西将会发生,然后它再次打印“1”。

因此输出将为1 ... 1

现在想想第二次调用Recursion,它会打印一个“2”,然后其他东西就会发生,然后再打印一个“2”。

所以它的输出是2 ... 2

将这两者放在一起,得到1 2 ... 2 1

然后继续前进。

答案 2 :(得分:1)

您应该尝试单步执行代码,使用铅笔和纸张(或虚拟便笺本)执行代码。让我们保持函数定义关闭:

void Recursion(int x)
{
1    if(x < 4) {
2        cout << x << " "; // 1st output: 1 2 3
3        Recursion(x + 1); 
.    }
. 
4    cout << x << " "; // 2nd output: 4 3 2 1
5    return;
}

现在,我们打电话给main

call main
    call Recursion(1)    -> x := 1
        (1) if (x < 4)   -> true
            (2) cout << x << " ";                                  // with x == 1
            (3) call Recursion(x + 1)    -> x := 2
                (1) if (x < 4)           -> true
                    (2) cout << x << " ";                          // with x == 2
                    (3) call Recursion(x + 1)    -> x := 3
                        (1) if (x < 4)           -> true
                            (2) cout << x << " ";                  // with x == 3
                            (3) call Recursion(x + 1)    -> x := 4
                                (1) if (x < 4)           -> false
                                (4) cout << x << " ";              // with x == 4
                            (5) end Recursion            -> x:= 3
                        (4) cout << x << " ";                      // with x == 3
                    (5) end Recursion            -> x:= 2
                (4) cout << x << " ";                              // with x == 2
            (5) end Recursion            -> x:= 1
        (4) cout << x << " ";                                      // with x == 1
    (5) end Recursion            -> del x
end main

现在,您可以检查输出的内容:cout << x << " ";连续调用值:1,2,3,4,3,2,1,产生"1 2 3 4 3 2 1 "

答案 3 :(得分:0)

如果你看一下它会有所帮助:

string RecursiveReturn(int x)
{
    if(x >= 4) {
        return to_string(x);
    }

    return to_string(x) + " " + RecursiveReturn(x+1) + " " + to_string(x);
}

int main()
{
    cout << Recursion(1);
}

递归调用就像任何其他函数调用一样...它被评估,并且当它返回调用函数时继续。

答案 4 :(得分:0)

enter image description here

该堆栈描述了当对stdout执行“1st output”时,“2nd output”被插入堆栈(“函数堆栈”)。

当函数“recursion”返回“main”函数时,会发生堆栈的展开或弹出内容。如果你正确地进行了透视,这就是它如何提供输出。

答案 5 :(得分:0)

这里发生的是前向和后向递归。

简单来说:在调用递归之前使用计数器(因为长计数器递增)通常是前向递归,而后使用counter是递归递归。

仅限前进:

void Recursion(int counter) {
  if(counter < n) {
    cout << counter << " ";
    Recursion(counter + 1); 
  }
}

首先打印计数器而不是一遍又一遍地调用递归...这导致 1 2 .. n-1

call : 1
print: 1
call : 1+1
print: 2
call : 2+1
print: 3
call : 3+1
if(...) // not true
return:
return:
return:
return:

仅向后:

void Recursion(int counter) {
  if(counter < stop) {
    Recursion(counter + 1);
    cout << counter << " ";
  }
}

首先一遍又一遍地调用Recursion,而不是打印计数器...这会导致 n-1 ... 2 1

call : 1
call : 1+1
call : 2+1
call : 3+1
if(...) // not true
return:
print: 3
return:
print: 2
return:
print: 1
return:

注意:使用if-statement 中的计数器会消除 n 的打印,它会在n-1上停止。

这是tree-traversal之类的遍历算法的基础,它被称为预订和后期订单。