您好我在cpp中写过这样的小递归方法。我想了解递归
void print(int n)
{
if(n==6)
return;
print(++n);
cout<<n<<endl;
//output is 6 5 4 3 2
}
void print(int n)
{
if(n==6)
return;
print(n+1);
cout<<n<<endl;
//output is 5 4 3 2 1
}
void print(int n)
{
if(n==6)
return;
print(n++);
cout<<n<<endl;
//programme crash
}
你能否解释一下我内心发生了什么?
答案 0 :(得分:2)
函数调用放在堆栈上。把它想象成一堆盘子。任何时候在你的代码中调用“print(x)”,它都是这堆板的另一个补充。当函数到达其结束的大括号或当它返回return语句时,它将从堆栈中删除。
我假设你在这些函数上调用print(0)。因此,print(0)是堆栈中的第一件事。你的上一个函数崩溃了,因为它“永远”地调用了print(0),直到它没有空间容纳更多的“板块”。这称为无限递归,由于堆栈的限制,无限递归很少是无限的。
至于其他函数,嗯,“cout”语句只被称为“after”函数从堆栈中删除。这些方法中的每一种都不断地将新东西放在堆栈上,只有一个例外是print(6)调用。这通常称为基本情况,并且是递归过程的结束。因为它开始从堆栈中逐步移除板块,这允许发生所有cout语句(与无限递归的情况不同)。
要理解这些代码的不同之处,你必须确保理解n ++,++ n和n + 1之间的区别。
答案 1 :(得分:1)
这不像递归中的问题那样是表达式评估中的问题。当n达到6时,我们有三个例程停止;否则,他们会做某种形式的增量和重复,表达方式不同。递归调用后,打印 n 的本地值并返回给调用者。
请注意,每次调用 print 时,都会向运行时堆栈添加新的本地变量空间。每个都有自己的 n 副本:递增一个不会改变其他副本。
答案 2 :(得分:0)
有时候,尝试说明呼叫顺序会很有帮助。
想象一下函数f1到f7的序列,其中f1调用f2调用f3,依此类推f7。 (因为我从0开始,而不是1)
f1看起来像:(和其他非常相似)
void f1(int n)
{
if(n==6)
return;
f2(++n);
std::cout<<n<<std::endl;
}
因此,由f1(0)启动的调用序列可能如下所示:
f1(0)--v : because f2 is called with ++n
f2(1)--v : because f3 is called with ++n etc.
f3(2)--v
f4(3)--v
f5(4)--v
f6(5)--v
f7(6)
每个函数调用都有一个返回:
// no cout of 7
return to f6(6)
cout<<... n is 6
return to f5(5)
cout<<... n is 5
return to f4(4)
cout<<... n is 4
return to f3(3)
cout<<... n is 3
return to f2(2)
cout<<... n is 2
return to f1(1)
cout<<... n is 1
现在,要转移到使用递归,只需用foo
替换编号的fvoid foo(int n)
{
if(n==6)
return;
foo(++n);
std::cout<<n<<std::endl;
}
和调用序列(用foo(0)启动)可能看起来像
foo(0)--v -- given 0, calls foo with 1
foo(1)--v -- give 1, calls foo with 2
foo(2)--v -- etc
foo(3)--v
foo(4)--v
foo(5)--v
foo(6) - return
我打电话给第一部分递归^^^^^^^^^^^^^^^^^^^^^^^^
return to foo(6) (was 5)
cout<<... n is 6
return to foo(5) (was 4)
cout<<... n is 5
return to foo(4) (was 3)
cout<<... n is 4
return to foo(3) (was 2)
cout<<... n is 3
return to foo(2) (was 1)
cout<<... n is 2
return to foo(1) (was 0)
cout<<... n is 1
第二部分,所有这些函数的返回,这个特定堆栈使用的“崩溃”,我有时会注意到'decursion'(旧军事术语的新用法)。 (++运算符的影响使得对齐误导。)
因为cout在递归调用之后(即在递归中),即使输入'n'在递归期间增加,输出序列也在减少。
因此,这个特定的序列cout每行1位数:
6
5
4
3
2
1
更新 - 为什么最后一段代码会崩溃?
最后一个代码片段崩溃,因为在递归调用之后发生了后递增。所以调用foo(0)会调用foo(n ++),但这只是再次调用foo(0),然后再次增加n(再次)。第二个和所有后续的foo()递归都看到相同的值0,因此终止条件(n == 6)永远不会发生,并且堆栈溢出了无限函数调用。