接函数(一),伙伴们大家好,欢迎大家继续C语言的学习,今天我们来讲解函数(二)的内容。本次内容包括:函数的嵌套调用和链式访问、函数的声明和定义、函数递归。
我们来进入正式内容:
五、函数的嵌套和链式访问
5.1嵌套调用
函数的嵌套,函数是可以相互调用的,也就是说一个函数可以调用另一个函数。主函数可以调用任意的函数,但是其他函数是不能调用主函数。另外,函数是可以嵌套调用,而不能嵌套定义。比如下面的代码:
函数的嵌套调用,在以后的文章中会经常用到,这里我们可以先熟悉一下,慢慢的就会熟悉函数的嵌套调用,嵌套调用就是,一个函数已经实现了一个功能,而另一个函数需要这个功能,那么我们就可以调用这个函数。如下代码:
5.2函数的链式访问
函数的链式访问就是把一个函数的返回值作为另一个函数的参数。
比如下面的代码:
这就是函数的链式访问!
六、函数的声明和定义
6.1函数声明:
函数的声明就是在告诉编译器有一个函数叫什么,返回类型是什么,参数链表是什么。但是这个函数还是不存在的,也就是说还没有定义。
函数在使用时,可以放在main函数的前面或者后面,但是当放在main函数后面时需要进行函数的声明,告诉编译器有这样一个函数。让它做好准备去调用这个函数。这就是先声明后调用,放在前面时就不需要了,因为编译器是按顺序进行编译代码的。
比如以下代码:
值得注意的是,我们在编写一个很大的项目时,会有很多人协同编写这个项目,那么我们如何才能简单又快速的把每个人的代码整合到一起呢,我们可以让程序员建立专属于自己的头文件和源文件,然后头文件里面写上函数的声明,那么我们就可以在主函数所在的源文件下包含此头文件,就可以把代码整合到一起。就不用再复制粘贴了。另外,我们可以把源文件生成一个静态库,来进行代码的保密。要使用该库时,就可以把该库和它的头文件放在项目目录下,项目源文件包含此头文件,那么我们就可以使用该库里的代码了。由于生成库时,里面存储的时机器语言,在文件里表现的是一堆乱码,人是根本无法读懂的,这样就实现了代码的保密性。
多人协作
test.h 头文件,里面包含函数的声明,#define定义宏,库函数所在的头文件,比如:
test.c放置函数的实现比如:
这种形式会在以后进行讲解。
生成静态库
6.2函数定义
函数定义就是实现函数的具体体现,交代了函数实现功能的细节。
七函数的递归
7.1递归的概念
在C语言中一个函数除了可以调用其他函数意外,还可以直接或间接的调用自己,这种函数自己调用自己的形式称为函数的递归调用。
7.2代码举例
下面通过练习来逐渐熟悉递归的使用。
接受一个整型值(无符号),按照顺序打印它的每一位。例如:
输入:1234,输出:1 2 3 4
代码示例:
#include <stdio.h>
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
int n = 0;
scanf("%d", &n);
print(n);
return 0;
}
这个代码的核心是:
1234要取到个位百位上的数时我们可以这样做:
1234%10 4
1234/10 123
123%10 3
123/10 12
12%10 2
12/10 1
1%10 1
这样就可以取出一个多位数字的每位数了。
关于递归,我们可以画图描述。
函数的递归顾名思义可以分为递推阶段和回归阶段。图中红色线代表递推阶段,蓝色线代表回归阶段。
递推阶段。将原问题不断分解为新的子问题,,逐渐从未知向已知的方向推进,最终到达一直的条件,即递归结束条件,这时递推阶段结束。
回归阶段。从已知条件出发,按照递推的逆过程,逐一求值回归,最终到达递推的开始处,结束回归阶段,完成递归调用。
编写函数不允许创建临时变量,求字符串的长度。
解决问题的思路,函数递归,图解:
代码示例:
不允许使用临时变量,求字符串长度,也就是说不允许创建临时变量count
思路 使用函数递归的方式是实现
int my_strlen1(char* str)
{
if (*str == '\0')
{
return 0;
}
else
{
return 1 + my_strlen1(str + 1);
}
}
int main()
{
char arr1[] = "hello world";
printf("%d\n", my_strlen1(arr1));
return 0;
}
7.3递归与迭代
循环是迭代的一种方式。
先做两个练习:
用递归求n的阶乘(不考虑异常)
这里可以使用一个公式:
当n <= 1时,n! = 1;当n > 1时,n! = n * factorial(n-1),这样我们就可以用递归的形式进行求解,
代码如下:
int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d", factorial(n));
return 0;
}
求第n个斐波那契数
这里也可以使用公式:
当n<=2时,此时斐波那契数为1,当n>2时,斐波那契数为:fib(n-2)+fib(n-1),我们就可以用递归来求解,代码如下:
int fib(int n)
{
if (n <= 2)
{
return 1;
}
else
{
return fib(n - 2) + fib(n - 1);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d ", fib(n));
return 0;
}
让我们来看一下,在求第40个斐波那契数时,第三个斐波那契数的计算次数。
我们只需要把函数修改成这样,定义一个全局变量count当n==3时count加1。
运行结果为:
可以看到,求第四十个斐波那契数时,计算第三个斐波那契数的次数为102334155,这样我们不得不担心一个问题,那就是栈溢出。我们都知道,函数在调用时,会在栈上实例化形参,这里调用了这么多次函数,虽然没有溢出,但是我们把n改为100呢,请看运行结果:
这里所耗费的时间会很长,最后程序会奔溃。stack overflow(栈溢出)
那么我们可以这样修改,这个代码。可以使用迭代的方式,分析:
当n <= 2时,return 1,当n > 2时 我们可以重复的执行 c = a+b, a = b, b = c 进行计算。
代码如下:
int fib1(int n)
{
int a = 1;
int b = 1;
int c = 0;
if (n <= 2)
{
return 1;
}
else
{
while (n > 2)
{
c = a + b;
a = b;
b = c;
n--;
}
}
return c;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d ", fib1(n));
return 0;
}
运行结果:
函数板块完毕,这里给大家留两个关于函数递归的经典题目:
1、汉诺塔问题。
2、青蛙跳台阶问题。
结语:积跬步,行千里。天天学习,天天进步。祝我们未来都好。
感谢大家的捧场:这里是我的代码库,可以去里面找寻源代码。谢谢大家。
https://gitee.com/junpei-c-language
制作不易,点点赞小伙伴们。