前言
前面两期我们讲到函数的库函数与自定义函数,并讲解了函数的形参与实参,这期我们就对函数做最后的讲解,话不多说,正文开始。
一、嵌套调用
1.嵌套调用定义
嵌套调⽤就是函数之间的互相调⽤,每个函数就⾏⼀个乐⾼零件,正是因为多个乐⾼的零件互相⽆缝的配合才能搭建出精美的乐⾼玩具,也正是因为函数之间有效的互相调⽤,最后写出来了相对⼤型的程序。
2.例题
例:假设我们计算某年某⽉有多少天?
3.如何思考
如果要函数实现,可以设计2个函数:
- is_leap_year():根据年份确定是否是闰年
- get_days_of_month():调⽤is_leap_year确定是否是闰年后,再根据⽉计算这个月的天数
4.编写历程
首先我们要知道每年每月多少天:
//1 2 3 4 5 6 7 8 9 10 11 12
//31 28 31 30 31 30 31 31 30 31 30 31
// 29(闰年)
我们看到如果是闰年,那么二月多一天;
所以我们先写get_days_of_month()函数:
int get_days_of_month(int y, int m)
{
int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; //此处开头加个0是因为让数组下标与月份保持一致
int day = days[m];
if (is_leap_year(y) && m == 2) //函数嵌套
day += 1;
return day;
}
int is_leap_year(int y)
{
if (((y % 4 == 0) && (y % 100 != 0)) || y % 400 == 0)
return 1; //闰年返回1
else
return 0;
}
这里的闰年之所以返回一是为了配合上面 get_days_of_month()函数的if函数,我们结合前面所学的布尔类型:C语言中的数据类型详解_c语言数据-CSDN博客文章浏览阅读1.8k次,点赞50次,收藏49次。学习C语言,要掌握的基础肯定离不开数据类型和变量。因为数据类型是用来约束数据的解释。通俗一点来说,数据类型决定了这个变量在程序中占的内存大小。而变量名是我们的程序对于可操作的存储空间的一个别名。提示:以下是本篇文章正文内容,下面案例可供参考C语⾔提供了丰富的数据类型来描述⽣活中的各种数据。例如:使⽤整型类型来描述整数,使⽤字符类型来描述字符,使⽤浮点型类型来描述⼩数。所谓“类型”,就是相似的数据所拥有的共同特征,编译器只有知道了数据的类型,才知道怎么操作数据。_c语言数据https://blog.csdn.net/2303_78558007/article/details/138294122?spm=1001.2014.3001.5501#:~:text=4.-,%E5%B8%83%E5%B0%94%E7%B1%BB%E5%9E%8B,-%E5%85%B6%E5%AE%9EC%20%E8%AF%AD%E2%BE%94%E5%8E%9F%E6%9D%A5
还可对is_leap_year()函数做如下改变:
_Bool is_leap_year(int y) //使用前要包含头文件:#include <stdbool.h>
{
if (((y % 4 == 0) && (y % 100 != 0)) || y % 400 == 0)
return true; //闰年返回真
else
return false;
}
5.代码展示
#include <stdio.h>
#include <stdbool.h>
int is_leap_year(int y)
{
if (((y % 4 == 0) && (y % 100 != 0)) || y % 400 == 0)
return 1; //闰年返回1
else
return 0;
}
//可使用布尔类型做改进
//_Bool is_leap_year(int y)
//{
// if (((y % 4 == 0) && (y % 100 != 0)) || y % 400 == 0)
// return true; //闰年返回真
// else
// return false;
//
//}
int get_days_of_month(int y, int m)
{
int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; //此处开头加个0是因为让数组下标与月份保持一致
int day = days[m];
if (is_leap_year(y) && m == 2) //函数嵌套
day += 1;
return day;
}
int main()
{
int year = 0;
int month = 0;
scanf("%d %d", &year, &month);
int days = get_days_of_month(year, month);
printf("%d\n", days);
return 0;
}
运行结果:
6.小总结
这⼀段代码,完成了⼀个独⽴的功能。代码中反应了不少的函数调⽤:
• main 函数调⽤ scanf 、 printf 、 get_days_of_month
• get_days_of_month 函数调⽤ is_leap_year
未来的稍微⼤⼀些代码都是函数之间的嵌套调⽤,但是函数是不能嵌套定义的。
二、链式访问
1.链式访问定义
所谓链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问。
2.一个简单的链式访问的例子
代码如下(示例):
#include <string.h>
int main()
{
printf("%d\n", strlen("abcdef"));
return 0;
}
3.一个有趣的代码
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
上面代码执⾏的结果是什么呢?
这个代码的关键是明⽩ printf 函数的返回是啥?
int printf ( const char * format, ... );
printf函数返回的是打印在屏幕上的字符的个数。
上⾯的例⼦中,我们就第⼀个printf打印的是第⼆个printf的返回值,第⼆个printf打印的是第三个
printf的返回值。
第三个printf打印43,在屏幕上打印2个字符,再返回2
第⼆个printf打印2,在屏幕上打印1个字符,再放回1
第⼀个printf打印1
所以屏幕上最终打印:4321
如果是这样的呢!
#include <stdio.h>
int main()
{
printf("%d", printf("%d ", printf("%d ", 43)));
return 0;
}
结果是什么?评论区揭晓;
三、函数的声明和定义
1.单个文件
⼀般我们在使⽤函数的时候,直接将函数写出来就使⽤了。
⽐如:我们要写⼀个函数判断⼀年是否是闰年。
//判断⼀年是不是闰年
int is_leap_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_leap_year(y);
if(r == 1)
printf("闰年\n");
else
printf("⾮闰年\n");
return 0;
}
上⾯代码中靠上的部分是函数的定义,靠下的部分是函数的调⽤。
这种场景下是函数的定义在函数调⽤之前,没啥问题
那如果我们将函数的定义放在函数的调⽤后边,如下:
int main()
{
int y = 0;
scanf("%d", &y);
int r = is_leap_year(y);
if (r == 1)
printf("闰年\n");
else
printf("⾮闰年\n");
return 0;
}
//判断⼀年是不是闰年
int is_leap_year(int y)
{
if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
return 1;
else
return 0;
}
这个代码在VS2022上编译,会出现下⾯的警告信息:
这是因为C语⾔编译器对源代码进⾏编译的时候,从第⼀⾏往下扫描的,当遇到第7⾏的is_leap_year函数调⽤的时候,并没有发现前⾯有is_leap_year的定义,就报出了上述的警告。
把怎么解决这个问题呢?就是函数调⽤之前先声明⼀下is_leap_year这个函数,声明函数只要交代清楚:函数名,函数的返回类型和函数的参数。
如:int is_leap_year(int y);这就是函数声明,函数声明中参数只保留类型,省略掉名字也是可以
的。代码变成这样就能正常编译了。
int is_leap_year(int y);//函数声明
int main()
{
int y = 0;
scanf("%d", &y);
int r = is_leap_year(y);
if(r == 1)
printf("闰年\n");
else
printf("⾮闰年\n");
return 0;
}
//判断⼀年是不是闰年
int is_leap_year(int y)
{
if(((y%4==0)&&(y%100!=0)) || (y%400==0))
return 1;
else
return 0;
}
函数的调⽤⼀定要满意,先声明后使⽤;
函数的定义也是⼀种特殊的声明,所以如果函数定义放在调⽤之前也是可以的。
2.多个文件
⼀般在企业中我们写代码时候,代码可能⽐较多,不会将所有的代码都放在⼀个⽂件中;我们往往会根据程序的功能,将代码拆分放在多个⽂件中。
⼀般情况下,函数的声明、类型的声明放在头⽂件(.h)中,函数的实现是放在源⽂件(.c)⽂件中。
如下:
add.h:
//函数声明
int Add(int x, int y);
add.c:
//函数定义
int Add(int x, int y)
{
return x + y;
}
如何使用:
#include "add.h" //包含头文件即可使用
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int ret = Add(a, b);
printf("%d\n", ret);
return 0;
}
其实这就类似于我们之前讲的库函数 ;
有了函数声明和函数定义的理解,我们写代码就更加方便了;
多文件书写好处是什么?
1.逻辑清晰
2.方便多人协同(每人做一个方面,各司其职,效率高)
3.适当隐藏代码(拓展)
总结
今天我们结束了对C语言函数的讲解,下期见!