对C语言中的递归感到困惑
我有以下代码:对C语言中的递归感到困惑,c,recursion,C,Recursion,我有以下代码: #include <stdio.h> void SingSongFor(int numberOfBottles){ if (numberOfBottles == 0){ printf("There are simply no more bottles of beer on the wall.\n\n"); } else { printf("%d bottles of b
#include <stdio.h>
void SingSongFor(int numberOfBottles){
if (numberOfBottles == 0){
printf("There are simply no more bottles of beer on the wall.\n\n");
} else {
printf("%d bottles of beer on the wall. %d bottles of beer.\n", numberOfBottles, numberOfBottles);
int oneFewer = numberOfBottles - 1;
printf("Take one down, pass it around, %d bottles of beer on the wall.\n\n", oneFewer);
SingSongFor(oneFewer); // This function calls itself!
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
}
}
int main(int argc, const char * argv[]) {
// We could sing 99 verses, but 4 is easier to think about
SingSongFor(4);
return 0;
}
#包括
void SingSongFor(整数瓶){
如果(NumberOfLabels==0){
printf(“墙上再也没有啤酒瓶了。\n\n”);
}否则{
printf(“%d瓶啤酒挂在墙上。%d瓶啤酒。\n”,numberof瓶装,numberof瓶装);
int oneless=瓶数-1;
printf(“取下一瓶,在墙上传%d瓶啤酒。\n\n”,少一瓶);
SingSongFor(oneless);//此函数调用自身!
//在函数结束前打印消息
printf(“将一个瓶子放在回收箱中,%d个空瓶子放在回收箱中。\n”,numberof瓶);
}
}
int main(int argc,const char*argv[]{
//我们可以唱99首诗,但4首更容易思考
SingSongFor(4);
返回0;
}
据我所知,程序必须在打印以下内容后终止:
墙上再也没有啤酒了
但它为什么会继续打印:
将一个瓶子放入回收站,1个空瓶子放入垃圾箱
把一个瓶子放进回收站,两个空瓶子放进垃圾箱
把一个瓶子放进回收站,3个空瓶子放进垃圾箱
将一个瓶子放入回收站,4个空瓶子放入垃圾箱
if函数已经打印出了一条消息,但是它没有终止消息,而是同时到达else。这怎么可能?“NumberOfVillages”是如何从1增加到4的呢
更新:这是我对代码的理解。如果我错了,请纠正我。
行后
SingSongFor(oneFewer); // This function calls itself
你有一张打印纸
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
这就是程序所做的。具有存储在堆栈上的numberoflabels
值。3瓶:
SingSong(3):
PRINT 2 LINES
SingSong(2):
PRINT 2 LINES
SingSong(1):
PRINT 2 LINES
SingSong(0):
PRINT 1 LINES
PRINT RECYCLE LINE
PRINT RECYCLE LINE
PRINT RECYCLE LINE
在您的最后一个内部递归调用发生后,它会通过每个方法调用退出,并调用循环消息。递归函数调用是堆叠的。所以是这样的: SingSongFor(4) | v SingSongFor(3) | v SingSongFor(2) | v SingSongFor(1) | v SingSongFor(0) SingSongFor(4) | v SingSongFor(3) | v SingSongFor(2) | v SingSongFor(1) | v SingSongFor(0)
最后一次呼叫显示没有更多瓶子,然后返回,这就是神奇的发生:它返回到上一次呼叫,然后打印关于回收箱的消息,然后返回到上一次呼叫,再次打印消息,依此类推。一切正常。当您到达最后一条消息
时,墙上已经没有啤酒瓶了。
您的程序返回到调用它的位置(它是在带有参数1
的函数SingSongFor
中调用的)。然后打印消息将一个瓶子放入回收箱,将一个空瓶子放入垃圾箱。
并返回上一次调用函数SingSongFor
,参数为2
。如图4所示。要了解程序为何如此运行,必须了解函数调用是如何工作的。它是递归的这一事实可能使编译器能够进行一些优化,从而提高程序的效率,但从概念上讲,递归并不能真正改变正在发生的事情
首先,让我们研究一个替代程序,它使用非递归函数调用,执行与您的程序基本相同的操作
void SingSongFor4(){
printf("4 bottles of beer on the wall. 4 bottles of beer.\n");
printf("Take one down, pass it around, 3 bottles of beer on the wall.\n\n");
SingSongFor3();
// Print a message just before the function ends
printf("Put a bottle in the recycling, 4 empty bottles in the bin.\n");
}
}
void SingSongFor3(){
printf("3 bottles of beer on the wall. 3 bottles of beer.\n");
printf("Take one down, pass it around, 2 bottles of beer on the wall.\n\n");
SingSongFor2();
// Print a message just before the function ends
printf("Put a bottle in the recycling, 3 empty bottles in the bin.\n");
}
}
void SingSongFor2(){
printf("2 bottles of beer on the wall. 2 bottles of beer.\n");
printf("Take one down, pass it around, 1 bottles of beer on the wall.\n\n");
SingSongFor1();
// Print a message just before the function ends
printf("Put a bottle in the recycling, 2 empty bottles in the bin.\n");
}
}
void SingSongFor1(){
printf("1 bottles of beer on the wall. 1 bottles of beer.\n");
printf("Take one down, pass it around, 0 bottles of beer on the wall.\n\n");
printf("Put a bottle in the recycling, 1 empty bottles in the bin.\n");
}
}
int main(int argc, const char * argv[]) {
// We could sing 99 verses, but 4 is easier to think about
SingSongFor4();
return 0;
}
我希望很明显,每个函数都会打印几行,调用下一个函数,然后打印另一行。每个被调用的函数依次执行此操作,因此,例如,将打印SingSongFor4()
的两行,然后调用SingSongFor3
。这将打印它的2行,然后调用SingSongFor2()
,它将打印它的行,依此类推SingSongFor1()
不调用任何其他函数,因此它会打印所有三行,然后返回到SingSongFor2()。当您按照函数调用“down”时,总共会在墙上看到8行X个瓶子/取下一个
,然后在反向返回“up”时看到4行“将瓶子放入垃圾箱”
你的函数没有什么不同,只是它被参数化了,并添加了一些逻辑来确定它什么时候应该像SingSongFor1()
一样工作,什么时候应该像其他3个一样工作。我说这没有什么不同,只是在你的例子中,你有一个程序文本的副本,它由程序的每次调用共享,而不是4个单独的(几乎相同的)文本副本。使共享文本副本成为可能的是每个函数调用的本地上下文—参数、变量和一些有关程序所在位置和程序执行状态的内务管理信息
通常,此上下文信息包含在称为堆栈的特殊数据结构中。之所以称之为堆栈,是因为你把东西一个叠在另一个上面,然后从“顶部”一次移除一个。每个堆栈帧包含函数一次调用的上下文:参数——在你的例子中是numberoflabels
;局部变量-oneless
;以及有关函数结束或返回时应执行哪个语句的信息。调用函数时,与该调用对应的帧被推送到堆栈上,并执行函数的文本。当它完成时,框架弹出,调用函数中的执行在其停止点(存储在弹出的堆栈框架中用于此目的)恢复。它将使用堆栈的新“顶部”框架恢复其上下文
不过,重要的是,递归函数的工作方式与任何其他函数完全相同——每次调用它时,它都会获得自己的新上下文,即使函数的文本是相同的。它执行到完成,然后返回到上一个上下文-可能是同一个函数,但具有不同的
SingSongFor(oneFewer); // This function calls itself!
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
SingSongFor(oneFewer); // This function calls itself!
#include <stdio.h>
#include <stdlib.h>
void SingSongFor (int numberOfBottles){
if (!numberOfBottles)
return;
int oneFewer = numberOfBottles - 1;
printf (" %d bottles of beer on the wall. %d bottles of beer.\n",
numberOfBottles, numberOfBottles);
printf (" Take one down, pass it around, %d bottles of beer on the wall.\n\n",
oneFewer);
if ((oneFewer) == 0)
printf (" There are simply no more bottles of beer on the wall.\n\n");
else
SingSongFor (oneFewer); // This function calls itself!
}
void ss_helper (int numberOfBottles)
{
SingSongFor (numberOfBottles);
/* Print a message just before the function ends */
printf(" Put a bottle in the recycling, %d empty bottles in the bin.\n",
numberOfBottles);
if (numberOfBottles >= 6)
printf ("\n Now go sober up you lush...\n\n");
}
int main(int argc, const char *argv[]) {
// We could sing 99 verses, but 4 is easier to think about
int coldbeers = (argc > 1) ? atoi (argv[1]) : 4;
// SingSongFor (coldbeers);
ss_helper (coldbeers);
return 0;
}
$ ./bin/beersong
4 bottles of beer on the wall. 4 bottles of beer.
Take one down, pass it around, 3 bottles of beer on the wall.
3 bottles of beer on the wall. 3 bottles of beer.
Take one down, pass it around, 2 bottles of beer on the wall.
2 bottles of beer on the wall. 2 bottles of beer.
Take one down, pass it around, 1 bottles of beer on the wall.
1 bottles of beer on the wall. 1 bottles of beer.
Take one down, pass it around, 0 bottles of beer on the wall.
There are simply no more bottles of beer on the wall.
Put a bottle in the recycling, 4 empty bottles in the bin.
SingSongFor(oneFewer); // This function calls itself!
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);