哈喽小伙伴们,帅气的我又来了哦~
今天我们来看看C中的函数是怎么个事~
① 函数的定义格式及其解释
话说,在C中所有的代码都是用函数来实现的,比如程序的入口: main函数 (这个在之前提到过喔~,不知道各位小伙伴是否记得呢~)
如果不记得的话,咱们一起来看看吧!
#include <stdio.h>
int main()
{
printf("hehe\n");
return 0;
}
如图所示,上面从第二行开始往下,就是main 函数的定义啦!
一般,函数的定义格式如下:
通过上面的批注,相信你已经大概了解了函数的定义格式,那么请各位小伙伴仿照这个批注说出main函数的各个部分是怎样的~ main函数的返回类型是整形(int),函数名为main ,没有形式参数(注:这里说的没有是指在C中一般不在main后面的括号里写参数,而不是说这个函数本身就没有参数),下面的大括号里面的代码统称为函数体(这里可以理解为函数的身体,也就是函数可实现的功能),这里可以看到,main函数的函数体也就是实现的功能是打印一个hehe 并换行, 至于后面的return 0 则与前面的返回类型int 相呼应.也就是说,在这个函数用完之后,会返回一个整数0 (当然,在这里return 0 是在main函数中固定的格式,此时不需要深究这个返回0的操作有什么用,只需要记住它是这么写的就OK了,后面在写其他函数时,会用到相对应的返回值,到时候各位小伙伴就知道返回值的作用,而对于这个main函数,则不用深究返回值的用途)
② 返回值的用途
接下来就给大家看看函数返回值到底是干什么的,咱们废话不多说,来看下面一串代码:
通过上面的批注,你明白了嘛? 这就是把is_leap_year 的返回值用于if语句中,这种方式也是使用返回值较为常见的一种方式~ 想必各位小伙伴已经理解了返回值的用途了吧~
下面来看看运行结果:
③ 形参和实参的区别
接下来我们来介绍形参和实参:
首先来看下面一串代码:
通过上面的注解,你明白了什么是形参,什么是实参了嘛?
如果还是不能理解,那么小编就用数学中的函数来类比吧! 希望可以用我有限的语言表达能力来尽量表达清楚这两者的意思.
在数学中,一个单元函数,eg: f(x)=x^2+2^x+1 在这个式子中,x是整个函数唯一的参数,而在C中却要说成整个函数唯一的形参,因为在这里,x 是一个泛指,可以代表所有的实数,所以它被称为形参
而我现在要计算f(1),如果在C中是不是要把这个1传给 x 呀,那这里的1就是实参.
也就是说,实参是形参的实例化.
不知我这样说,各位小伙伴能不能理解~
如果能够理解的话,那咱们现在来看看它们之间的区别~
这个区别用一句话来概括就是:形参是实参的一份临时拷贝.
啥意思呢? 我们还是根据上面这个add 函数的代码来看:调试结果如下:(关于调试内容后期细讲~)
源代码:
int add(int x, int y)
{
return x + y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = add(a, b);
printf("%d\n", c);
return 0;
}
从上面可以看出,x和y只是拷贝了a 和 b 的值,但是 x ,y, a, b 的地址各不相同,也就是说,a 和 b 只是把值传给了x 和y 在这之后,x 和 y 变成了两个不依赖于a ,b的两个独立的变量,所以我在自定义函数的内部一切都只是对x ,y进行的操作,而不影响 a 和 b 这两个变量.
如果有小伙伴不能理解我说的啥意思,那么来看下面一个错误代码的示范 :
这是一个交换算法的错误示范,其目的是通过调用swap 函数来实现a 和b 中的值的交换, 在这里可以看到,调用之前和调用之后,a 和b 中的值并没有交换,这是因为 在调用函数时,把 10 和 20 作为值的x 和y 变成了两个独立的变量,这时在swap 函数的内部完成的是对x 和y 值 的交换,与a 和 b 无关 .所以调用之后a 和b的值没有进行交换.不知各位小伙伴能否理解~(至于怎样解决这个问题,后期涉及指针的时候再细讲~,各位小伙伴稍安勿躁哈~)
④ 函数的嵌套调用和链式访问
了解了实参和形参的区别,我们接下来看看什么是函数的嵌套调用和链式访问~
⑴ 嵌套调用
啥叫函数的嵌套调用? 我们知道,C 程序是由一个个的函数组成的,而想要完成比较复杂的功能,这些函数必然不是孤立存在的,必然是你中有我,我中有你,一起协同来完成某个庞大而复杂的工程,咱们废话不多讲,一起来看下面一串代码:
int main()
{
printf("hehe\n");
return 0;
}
看到这里,有的小伙伴就比较晕了~ 这不是一个简单的打印操作嘛,怎么算得上嵌套调用呢? 兄弟,好好看看哦~ 这里的main 是不是主函数? 而printf是不是定义在C的标准库中的函数? 这不就是在main函数里面调用了一个printf函数嘛~ 这不就是函数的嵌套调用嘛~
所以,其实小伙伴们发现,我们每天都在写嵌套调用对不对? 这也印证了一个道理,我们每天在做的事情或许在当下看起来只有很小的作用,但在未来的某一天里,你一定会突然发现这一件件小事都能起到意想不到的作用. 所以加油吧,屏幕前的你!
⑵ 链式访问
知道了函数的嵌套调用,再让我们来看看什么叫链式访问吧
首先,所谓链式访问,顾名思义,就是把一个个函数像链条一样拼在一起然后进行访问. 咱们同样废话不多说,直接来看这样一串代码:
瞧瞧,这就是所谓的链式访问,在这里的printf 和strlen 是不是像链条一样接在一起呀~
接下来给出链式访问精确的定义:所谓函数的链式访问,就是把一个函数的返回值作为另一个函数的参数. 各位小伙伴看一看,这里是不是把strlen 的返回值作为printf 的参数呀~
⑶ 举例
接下来看看这两个板块的两个典型例子~
练习: 计算某年某月有多少天
针对这个练习,咱们直接来看下面一串代码:
//练习:计算某年某月有多少天
int is_leap_year(int year)
{
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return 1;
}
return 0;
}
int get_day(int year, int month)
{
int daysofordinary_year[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = daysofordinary_year[month];
if (is_leap_year(year) && month == 2)
{
day++;
}
return day;
}
int main()
{
int year = 0;
int month = 0;
scanf("%d %d", &year, &month);
int day=get_day(year, month);
printf("%d\n", day);
return 0;
}
要完成这个练习,请各位小伙伴想一想,其实除了2月以外,其他月份的天数是不是不随着年份的改变而变化呀~
所以咱们先不管那么多,先把平年的设计出来再说~
怎样设计呢? 其实小伙伴们可以看看我上面 get_day 函数的第一行代码,是不是把平年所有月份的天数放在了一个名叫daysofordinary_year 的数组里面呀~
然后这里有一个巧妙的设计就是,我为了让每一个天数元素的下标与它的月份相等,在这个数组的最前面添加了一个0,这样操作之后大家想,1月的天数所对应的下标就是1,这样做以后,我是不是就可以用month作为数组的下标,并且每个month正好对应每个月的天数呀~
以上是平年计算的思路,那闰年呢?
我们知道,平年和闰年唯一的区别是2月,闰年的2月比平年的2月多1天,所以在后面还要判断这个下标所对应的是不是闰年的2月,如果是,则天数在原先的基础上自增1. 不知各位小伙伴能否理解~
如果能理解的话,那么各位小伙伴可以从全局上看看:我在main函数里调用了get_day 函数,在get_day 函数里调用了is_leap_year 函数,这是不是函数的嵌套调用呀~
明白了这个例子,那咱们来看看下一个吧~
//练习:判断代码输出结果
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
要想完成这个练习,首先,大家想应该怎么入手呢?
如果有小伙伴想不出来,那我来类比一下数学吧~ 在数学中的复合函数,eg: f [g(x)] 一般像这种复合函数的题是怎么处理的呀? 是不是从里向外的原则,先看g(x) 所对应的值,再把它的值带入f(x)的表达式中呀?
那么大家看,这个代码的逻辑是不是跟这种复合函数差不多呀~ 就是printf 复合多个printf 呗~
明白了这个道理,我们再来看一个比较容易忽略的点,: %d 打印的是后面参数的返回值,而对于每一个printf 函数来说,它首先得打印后面的内容,然后自身还要返回一个值(这个返回值实际上是它所打印的字符的个数),这时,在它外层的printf 打印的是不是这个printf的返回值呀~
经过这一系列分析,我想你已经明白了:最内层的printf 会打印43 并返回一个整数2,所以倒数第二层的printf 打印的是一个整数2 并返回一个整数1 然后最外层的printf打印一个1 返回值为1(只不过最外层的返回值没有用到而已) 而由于打印之间没有换行,所以打印结果为:4321
下面来验证一下:
你看,结果是不是跟我们预想的一模一样呀~ 你听懂了嘛~
好啦,咱们这一篇先到这里,我们下一篇再见~
另外,这是这一篇所有的源码~
#include <stdio.h>
int main()
{
printf("hehe\n");
return 0;
}
int is_leap_year(int year)
{
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
{
return 1;
}
return 0;
}
int main()
{
int year = 2024;
if (is_leap_year(year))
{
printf("%d年是闰年\n", year);
}
else
{
printf("%d年不是闰年\n",year);
}
return 0;
}
//写一个名为add的加法函数.
int add(int x, int y)
{
return x + y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = add(a, b);
printf("%d\n", c);
return 0;
}
//错误的交换算法
void swap(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("%d %d\n", a, b);
swap(a, b);
printf("%d %d\n", a, b);
return 0;
}
//函数的嵌套调用:在main函数里面嵌套调用其他函数
int main()
{
printf("hehe\n");
return 0;
}
//函数的链式访问
#include <string.h>
int main()
{
char arr[] = "abc";
printf("%d\n",strlen(arr));
return 0;
}
//练习:计算某年某月有多少天
int is_leap_year(int year)
{
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return 1;
}
return 0;
}
int get_day(int year, int month)
{
int daysofordinary_year[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = daysofordinary_year[month];
if (is_leap_year(year) && month == 2)
{
day++;
}
return day;
}
int main()
{
int year = 0;
int month = 0;
scanf("%d %d", &year, &month);
int day=get_day(year, month);
printf("%d\n", day);
return 0;
}
//练习:判断代码输出结果
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}