第九章 函数
函数定义
- 函数原型
function prototype
:表明函数的类型 - 函数调用
function call
:表明在此处执行函数 - 函数定义
function definition
:表明函数要做什么
一些细节
- 函数声明可以置于
main
函数前面,也可以放在main
函数的声明变量处 - 注意,如果函数结尾没有
;
表明这是一个函数定义,而不是调用函数或者声明函数原型 - 你可以把函数和
main()
放在同一个文件,也可以把它们放在两个文件中。放在一个文件的单文件形式容易编译,而使用多个文件方便在不同的程序中使用同一个函数。 - 函数中的变量时局部变量
local variable
,意思是该变量只属于这个函数,我们可以在程序中其他地方使用这个变量,不过它们是同名的不同变量,不会引起冲突
函数体结构
==如果把函数放在一个单独的文件,要把
#define
和#include
指令也放入该文件==,如下面的函数体结构
image.png
调用函数
被调用的函数不关心传入的数值是来自常量、变量还是一般表达式。实际参数actual argument
是具体的值,该值要赋给作为形式参数的变量。
因为被调用函数的值是从主调函数中拷贝而来,所以无论被调用函数对拷贝数据进行什么操作,都不影响主调函数中的原始数据。
递归
C允许函数调用它自己,这种调用过程被称为递归recursion
。如果递归代码中没有终止递归的条件测试部分,一个调用自己的函数会无限递归。
可以使用循环的地方通常都可以使用递归,有时候用循环解决问题比较好,有时候用递归更好。递归方案更简洁但是效率却没有循环高。
1.递归的注意点
- 每级函数调用都有自己的变量,也就是每一层使用的相同名称的变量不同,它们对应的地址值也不同
- 递归函数中位于递归调用之前的语句,按照被调函数的顺序执行
- 递归函数中位于递归调用之后的语句,按照被调函数相反的顺序执行
- ==虽然每级递归都有自己的变量,但是并没有拷贝函数的代码==,程序按顺序执行函数中的代码,而递归调用就相当于又从头开始执行函数的代码,==除了为每次递归调用创建变量外,递归调用非常类似于一个循环语句==。
2.尾递归
最简单的递归形式是将递归调用置于函数的尾部,即正好在return
之前,尾递归是最简单的递归形式,它本身相当一个循环。
3.递归和倒序计算
在处理倒序问题时,递归比循环简单。
举个例子,我们需要编写一个函数,打印一个整数的二进制数。
- 在二进制中,奇数的末尾一定是1,偶数的末尾一定是0,所以对于数字n,通过
n % 2
即可确定n的二进制最后一位是1还是0 - 获取下一个数字的方法:要获取下一位数字必须把原数除以2,这种方法相当于在十进制下把小数点左移一位,然后对该数除以2的余数判断下一位是0还是1
- 如何停止:当与2相除的结果小于2时停止计算
实现函数:
#include<stdio.h>
void to_binary(unsigned long n);
int main(void)
{
int num = 0;
printf("please input a number: \n");
scanf("%d", &num);
to_binary(num);
return 0;
}
void to_binary(unsigned long n) /* 递归函数 */
{
int r;
r = n % 2;
if (n >= 2)
to_binary(n/2);
putchar(r == 0 ? '0' : '1');
}
4.递归的优缺点
递归优点是为某些编程问题提供了最简单的解决方案,缺点是一些递归算法会快速消耗计算器的内存资源。另外,递归也不方便进行阅读和维护。
举个例子,斐波那契数列(每一个数都是前两个数字之和):
#include<stdio.h>
unsigned long Fibonacci(unsigned n);
int main(void)
{
unsigned num = 0;
printf("please input a number: \n");
scanf("%d", &num);
unsigned long result = Fibonacci(num);
printf("the result is: %ld\n", result);
return 0;
}
unsigned long Fibonacci(unsigned n)
{
// 双递归
if (n > 2)
return Fibonacci(n-1) + Fibonacci(n-2);
else
return 1;
}
在这个函数中,假设我们调用了FIbonacci(40)
,那么第一级调用创建了变量n
,它会调用两次函数,在二级递归中分别创建两个变量,第三级递归中又会创建四个变量。每级递归创建的变量都是上一级递归的两倍,所以变量的数量呈指数型增长,很快会消耗计算机的大量内存从而使得程序崩溃。