对C中的递归感到困惑

我有以下代码:

#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;
}

根据我的理解,打印后程序必须终止:

There are simply no more bottles of beer on the wall.

但是如何恢复打印:

Put a bottle in the recycling, 1 empty bottles in the bin.

Put a bottle in the recycling, 2 empty bottles in the bin.

Put a bottle in the recycling, 3 empty bottles in the bin.

Put a bottle in the recycling, 4 empty bottles in the bin.

如果函数已经打印出一条消息,而不是终止它,它也会返回到其他的.这怎么可能?而在“numberOfBottles”中如何从1增加到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()的2行被打印,然后SingSongFor3被调用.这打印它的2行,然后调用SingSongFor2(),它打印它的行等等. SingSongFor1()不调用任何其他函数,因此它打印所有三行,然后返回到完成的SingSongFor2()等等.在你所有的8个X瓶的瓶子上,当你按照功能调用“向下”,然后4行“把一个瓶子放在垃圾桶”,当你返回“反向”的方向一个下来.

你的功能没有任何区别,除了它被参数化,并添加了一点逻辑来确定它应该像SingSongFor1()的行为以及它应该像另一个3.我说这没有什么不同,除了你的情况,你有每个程序调用共享的程序文本的单个副本,而不是4个单独(几乎相同)的文本副本.可以共享文本的副本是每个函数调用的本地上下文 – 参数,变量和有关程序所在位置的一些内务信息以及程序的执行状态.

通常,该上下文信息包含在称为堆栈的特殊数据结构中.它被称为堆栈,因为你堆叠在另一个之上的东西,然后从“顶部”一次删除它们.每个堆栈帧包含一个函数调用的上下文:参数 – 在你的caseOfBottles中;局部变量 – oneFewer;以及当功能结束或返回时应该执行哪个语句的信息.当一个函数被调用时,与该调用相对应的框架被推入堆栈,并且该函数的文本被执行.当它完成时,框架被弹出,并且在调用功能中,它在它离开的位置恢复执行(为此,它被存储在弹出的堆栈框架中).它恢复使用堆栈的新的“顶部”框架的上下文.

重要的是,您的递归函数的工作方式与任何其他函数完全相同 – 每次调用它都会获得一个自己的新上下文,即使函数的文本是相同的.它执行完成,然后返回到上一个上下文 – 这可能是相同的功能,尽管具有不同的上下文.

转载注明原文:对C中的递归感到困惑 - 代码日志