【C语言】函数解析(2)

书接上回,这次继续介绍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;
}

运行结果:

c289dc9b4f66465c995ab1ba8cfead43.png未来我们在编写稍微大一些的代码中都会运用到函数的嵌套调用,这需要我们在一天天的编写中慢慢熟练。

还有一个易错点:函数可以嵌套调用,但不能嵌套定义

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函数返回的是什么。

98540407ffcc424fac4ff59a19f5367d.png

b1add187f0b743c7a37a79b75cc97010.png

可见,printf函数返回的是打印在屏幕上的字符的个数。

本例中,第一个printf打印的是第二个printf的返回值,第二个printf打印的是第三个printf的返回值。

第三个printf打印43,在屏幕上打印两个字符,返回2

第二个printf打印2,在屏幕上打印一个字符,返回1

第一个printf打印1

所以最终的打印结果为:

094b6e921e904ba6b8279a4c05c5bd47.png

是不是很神奇?

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上搜索下面两个拓展问题

递归拓展:

  • 汉诺塔
  • 青蛙跳台阶

函数——完。

:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值