一、嵌套调用
函数的嵌套调用就是在一个函数内部调用另一个函数,函数可以嵌套调用但不能嵌套定义。如下图,我想打印3行内容,首先调用了printThreeLine函数,然后在该函数中每次循环都调用一次newLine函数,这样就打印了3行内容。合理利用嵌套调用可以将一个复杂的功能拆分为多个简单的小功能,使得每个函数的功能更加独立。
二、链式访问
链式访问是将一个函数的返回值作为另一个函数的参数,无返回值的函数不能作为其他函数的参数。下面是一个经典的链式访问的例子。printf函数的返回值为打印的字符个数,在下图中,外层的printf函数需要依靠内层printf函数的返回值才能确定要打印的数值。最内层的printf函数打印43并返回2;第二层printf函数接收到返回值2并打印,同时返回1;最外层printf函数接收到返回值1后打印。所以屏幕上最终输出4321。链式访问中,如果内层的某个函数在调用过程中出现了问题,会依次影响到外层函数的执行结果,在调试过程中需要注意这一问题。
三、递归
递归通俗来讲就是函数自己调用自己,当满足一定的条件后停止对自身的调用。递归能够将一个大型复杂的问题转化为与原问题相似的规模较小的问题,只需用少量代码就可以描述出多次重复的计算过程,增加了代码的可读性但运行时的开销要比正常使用循环实现大。下面我们来看一个用递归实现冒泡排序(从小到大)的例子。
void bubbleSort(int arr[], int len) { if (len == 1) //终止递归的条件 即递归出口 当数组长度为1时无需排序 return; int flag = 1; for (int i = 0; i < len - 1; i++) { int tmp; if (arr[i] > arr[i + 1]) { tmp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = tmp; flag = 0; } }//循环结束后 最右边存放一轮排序中的最大值 if (flag)//若flag的值没有发生改变 说明排序时一次交换都没有发生 数组已经是有序排列的 return; else bubbleSort(arr, len - 1); } int main() { int arr[10] = { 2, 3, 1, 4, 9, 6, 7, 5, 8, 0 }, len = sizeof arr / sizeof (int); bubbleSort(arr, len); for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } return 0; }
一轮排序结束后,我们在下一次调用时将len - 1作为参数,使数组最后一位与数组前面的数分割开来,把数组前面的数看作一个新的整体,继续在这个整体中找出最大值,接着又把最后一位与前面的数进行分割。一直重复以上动作,直到len的值变为1或者循环没有发生交换时停止。
从这个例子中我们可以发现,想要使用递归必须明确两个条件:
- 终止递归的条件(递归出口)
- 递归表达式(找出规律)
在递归中通常将问题分割成两个部分(1和整体的思想),掌握这种思想能够让我们快速地找出递归表达式。
根据实际需求,合理利用以上三种方法能够让我们编写的代码结构更加清晰明了,便于后期的维护和调试,从而提升自己的编程能力。