书接上回,这次继续介绍C语言中函数的进阶技巧。
1.函数的嵌套调用与链式访问
1.1嵌套调用
嵌套调用其实就是函数之间的相互调用,就像机器内部的一个个零件,多个零件的相互配合才能成就机器的顺利运作,而也正是函数之间的有效调用,最后才能写出相对大型的程序。
例如:给出一个年份月份,请你计算这个月有多少天。
我们可以通过以下两个函数来实现。
- is_year():确定是否为闰年
- get_days:确定是否为闰年后,再根据月份计算天数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int is_year(int y) {
if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
return 1;
else
return 0;
}
int get_days(int y, int m) {
int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = days[m];
if (is_year(y) && m == 2)
day++;
return day;
}
int main()
{
int y = 0;
int m = 0;
printf("请输入年份与月份:\n");
scanf("%d %d", &y, &m);
int d = get_days(y, m);
printf("天数为%d\n", d);
return 0;
}
运行结果:
未来我们在编写稍微大一些的代码中都会运用到函数的嵌套调用,这需要我们在一天天的编写中慢慢熟练。
还有一个易错点:函数可以嵌套调用,但不能嵌套定义
1.2链式访问
所谓链式访问就是将一个函数的返回值作为另一个函数的参数,就像链条一样把函数串起来。
例如:
#include <stdio.h>
#include<string.h>
int main()
{
int len = strlen("abcdef");
printf("%d\n", len);
return 0;
}
在这段代码中,我们利用了两条语句才得到了我们想要的结果,而在链式访问优化后呢?
#include <stdio.h>
#include<string.h>
int main()
{
printf("%d\n", strlen("abcdef"));
return 0;
}
我们直接把strlen的返回值作为printf函数的参数,使得代码更加简洁,省去了创建变量的步骤。
我们再看一个有趣的代码
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
得到答案的关键就在于明白printf函数返回的是什么。
可见,printf函数返回的是打印在屏幕上的字符的个数。
本例中,第一个printf打印的是第二个printf的返回值,第二个printf打印的是第三个printf的返回值。
第三个printf打印43,在屏幕上打印两个字符,返回2
第二个printf打印2,在屏幕上打印一个字符,返回1
第一个printf打印1
所以最终的打印结果为:
是不是很神奇?
2.函数的声明与定义
在前一篇文章中,我们了解到了函数是如何定义的,那函数的声明是什么呢?
仍然是上文的例子:判断闰年函数
#include <stdio.h>
int is_year(int y) {
if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
return 1;
else
return 0;
}
在这里,我们把去掉函数体的这一部分称为函数声明:
int is_year(int y);
而上面的一整段代码则是函数定义,也就是函数声明加上函数体。
那为什么我们要单独提到函数声明呢?下面让我们看看它在不同场景下的作用。
2.1单文件
define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int is_year(int y) {
if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
return 1;
else
return 0;
}
int main()
{
int y = 0;
scanf("%d", &y);
int r = is_year(y);
if (r == 1)
printf("闰年");
else
printf("非闰年");
return 0;
}
在上面这段代码中,我如之前一样,将函数的定义放在了函数的调用之前,没啥问题。
那如果我们将定义放在函数调用之后呢?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int y = 0;
scanf("%d", &y);
int r = is_year(y);
if (r == 1)
printf("闰年");
else
printf("非闰年");
return 0;
}
int is_year(int y) {
if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
return 1;
else
return 0;
}
这时我们再运行程序,程序便会报错:
这是因为C语言编译器在对源代码进行编译时,是从第一行往下扫描的,当遇到我们的is_year函数时,并没有在前面发现它的定义,就报出了警告。
那有办法解决吗?很简单,只要我们在函数调用前先声明一下函数就行了。
代码变成这样便能正常编译了
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int is_year(int y);
int main()
{
int y = 0;
scanf("%d", &y);
int r = is_year(y);
if (r == 1)
printf("闰年");
else
printf("非闰年");
return 0;
}
int is_year(int y) {
if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
return 1;
else
return 0;
}
所以,函数的调用一定要满足:先声明后使用 (函数的定义也是一种特殊的声明)
到这里,你可能会有疑问:我们把函数定义在调用之前不就行了,为什么还需要声明?
不用着急,下面我们看看更复杂的情况。
2.2多文件
一般在企业中写代码时,代码可能比较多,不会把所有代码都放在一个文件中。我们往往会根据程序的功能将代码拆分放在多个文件中。
一般情况下,函数的声明,类型的声明放在头文件中(.h),函数的实现放在源文件中(.c)。
如下:
add.c
int add(int a,int b)
{
return a+b;
}
add.h
int add(int a,int b);
test.c
#include<stdio.h>
#include"add.h"
int main()
{
int a = 3;
int b = 5;
int c = add(a,b);
printf("%d",c);
return 0;
}
运行结果:
理解了函数的声明与定义,我们写代码就更加方便了。
3.函数递归
递归是学习C语言绕不开的一个话题,那么什么是递归呢?
在C语言中,递归其实就是函数自己调用自己。
写一个最简单的递归函数
#include<stdio.h>
int main() {
printf("haha");
main();
return 0;
}
这就是一个简单的递归函数,在打印完haha后再次调用main函数,一直死循环,直到栈溢出。
这段代码虽然是一个问题代码,但也向我们展示了递归的基本原理 。
3.1递归思想
把一个大型的复杂问题转化成一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归也就结束。这就是递归的基本思想,也就是大事化小,小事化了。
3.2递归举例
例:求n的阶乘
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。
用公式来表达就是:n!=(n)*(n-1)*(n-2)……3*2*1
而在我们细细观察后,是可以将该公式化简为另一形式
例如:5!=5*4*3*2*1
4!=4*3*2*1
所以 5!=5*4!
也就是n!=(n)*(n-1)!
这样我们就把原问题转换成了一个规模更小的问题。
于是我们就能写出函数fact求n的阶乘
int fact(int num) {
if (num == 0)
return 1;
else
return num*fact(num - 1);
}
完整代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int fact(int num) {
if (num == 0)
return 1;
else
return num*fact(num - 1);
}
int main()
{
int n = 0;
scanf("%d", &n);
int r = fact(n);
printf("%d", r);
return 0;
}
运行结果:
如此,我们便完成了自己的第一个递归代码,大家看完后可以自己尝试编写求斐波那契数的函数,以便加深自己对递归的理解。
也可以在csdn上搜索下面两个拓展问题
递归拓展:
- 汉诺塔
- 青蛙跳台阶
函数——完。
:)