C语言入门第16课:特殊的函数调用

  我们已经学习过了几个标准库函数的使用,也学习了自己定义一个函数来实现我们想要的功能,不知道你是否有注意到一点:我们在调用函数时,都是在一个函数中调用另一个甚至多个其他函数来实现我们想要的功能。是不是函数的调用必须遵循这样的规则呢?如果在我们自定义的函数内调用函数自身会怎么样呢?今天我们就来学习函数的这种用法:在函数内部调用函数自己,即函数的递归调用。

  编程语言的起源中,有一个重要的作用是为了解决现实世界的问题,即使是现在,编程也有很多时候是为了解决实际的问题。解决一个问题可能有很多种方法,但有一个方法应该很容易就能想到,那就是“分治法”,即把一个较为复杂的问题分解为多个不那么简单的问题,而后多个简单的问题再次分解为更加简单的问题,重复这一过程直到最终分解的问题能够得到显而易见的答案,再将答案逐层反馈到最初的问题,那么最初的问题就得到了解答。函数的递归调用就可以看做是对这一思想的直接运用,通过于一次次的递归调用降低问题的复杂度,直到函数能够直接给出答案,再最终将结果返回到第一次调用的目标。

  听上去应该不太复杂,为了我们能够理解递归调用这个处理过程,我们需要介绍一种现实存在的计算问题,它非常适合用递归来实现,并且比较容易理解。也许有些人听过这种计算:阶乘。

  阶乘是一种针对自然数的乘法计算,一个自然数n的阶乘记作n!,它代表的意义是从1开始连续相乘到n的结果,特别的,0!等于1。想必从这个定义当中你已经发现了,自然数n的阶乘实际上就是n乘以n-1的阶乘,那么要通过函数来计算n得而阶乘,就可以通过n-1的阶乘结果乘以n,由于n是已知的,那么只需要计算n-1的阶乘,而n-1的阶乘依然可以重复上述的过程,直到只需要计算1的阶乘时,我们知道1的阶乘是1,把答案一路返回,答案就得到了。描述起来似乎还是有些复杂,我们来看代码示例吧:

  

  上例中,函数factorial是阶乘计算函数,我们在函数中加上了一些打印来跟踪函数的调用和计算过程。一般的,我们在调用函数的时候,函数得而计算发生在函数内部,执行结果是“顺序”的,较容易理解。但递归调用则与此印象相反,递归调用函数时,在最终的显而易见的计算结果给出之前,所有的函数调用都处在等待结果的过程中;在最终的简单结果得到后,才会“倒序”完成函数的计算过程,并最终给出计算结果。听起来有点绕,我们先来看例子当中的代码,在用户输入一个数字后,main函数中获得了它并赋值给变量num,然后调用函数factorial,这时,在函数factorial内,首先判断num是否为0,如果不是,则再次调用函数factorial并将num-1后传入,注意,这时在第一层的调用中,并没有得到计算结果,只是再次进入函数factorial计算num-1,直到传入的函数factorial中的数字为0时,直接返回了结果1,这时,结果会逐层返回并乘以之前记录的数字,直到返回第一层,并得到最终的计算结果。具体过程如下图所示:

  

  可以看到在调用递归函数时,它的调用过程与计算过程实际上向一对从中间对称的镜像过程,最早的调用最后才计算。这一特点使得递归函数有这广泛的是用空间,我们以后还会接触到表达式的计算、汉诺塔计算等等,都会对递归函数情有独钟。

  好了,今天递归就学习到这里了,有任何问题都欢迎在评论区多多交流。

  以前的课程请点击这里:

  C语言入门第15课:多维数组与指针使用

  C语言入门第14课:C的灵魂——指针

  C语言入门第13课:“组团”的数据

  C语言入门第12课:单字符的输入输出

  举报/反馈