ʕ • ᴥ • ʔ
づ♡ど
🎉 欢迎点赞支持🎉
文章目录
[3.1 嵌套调用](#3.1 嵌套调用)
[3.2 链式访问](#3.2 链式访问)
[4.1 单个文件](#4.1 单个文件)
[4.2 多个文件](#4.2 多个文件)
前言
大家好啊,继我们上一次讲的函数(1)已经过了两周了,不知道大家掌握的怎么样,由于要期末考试,我断更了两周,希望大家见谅,那么我们接着上一章节来继续说说函数的内容吧。我们这一章节就来看看return语句和数组传参及嵌套调用和链式访问等问题,接下来我们来一起学习本次的内容。
一、return语句
我们在使用函数的过程中,总是使用到return语句,例如
cs
int Add(int a, int b)
{
int c = a + b;
return c;
}
既然return这么重要,那它到底怎么使用呢,我们来了解了解。
return后面可以是一个数值,也可以是一个表达式,但是如果return后面是表达式的话要先计算表达式的内容,在返回结果。
return后面可以什么都没有,直接写return,这种写法适合函数返回类型是void(无类型返回)的情况
return返回的值和函数返回的类型不一致,系统会自动返回的值转换成函数返回的类型。
return执行后,函数就彻底返回,后面的代码不执行。
如果函数中出现了if分支,要确保每个分支都有返回的情况,不然程序会报错。
我来一条一条的为大家解释。
首先是第一条,我们可以从上节课的代码就能知道,return可以直接返回数值,也可以返回一个表达式。
cs
int Add(int a, int b)
{
int c = a + b;
return c;
}
cs
int Add(int a, int b)
{
return a + b;
}
表达式是先通过计算出结果后才返回最终的值的,这很好理解,就不多赘述。
其次是第2条也很好理解,函数本身是不需要任何的返回类型的,那return后面肯定什么也不能加的,可能会有人疑问那这个return加在这个函数里面有什么用,当然是有用的这个就可以和第4点结合起来,它可以直接中断函数而不继续运行下面的代码。这就是这个return的作用。
cs
void Print(int n)
{
if (n <= 0)
{
return;
}
for (int i = 1; i <= n; i++)
{
printf("%d ", i);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
Print(n);
return 0;
}
我们可以来看这个代码,这个代码的目的是如果n>0就把1~n的数字打印出来,我们可以来看这个打印的函数Print就可以看到,如果n<=0就return,我们可以来看看这个return后还会不会继续运行函数下面的代码了。
可以看到,我输入一个正数它很好的打印出来了,那我输入一个负数或者0会怎么样呢?
很显然,它什么都没有输出,这就是return的强大,它比break都强,break是跳出循环,而return是直接跳出函数。
我们现在来看看第3点,其实也很好理解,就是如果return返回的类型和函数要求的返回类型不一样是会转换成函数类型。
cs
char test(char a)
{
int b = a;
return b;
}
int main()
{
char a;
scanf("%c", &a);
int b = sizeof(test(a));
printf("%d", b);
return 0;
}
我们可以来看看这个代码,我们先输入一个字符,然后写一个函数,这个函数的返回类型是字符型,但是我却返回了一个整型,那我们来看看它到底返回的是字符还是整型吧,我们可以知道字符的sizeof是1,而整型是大于等于2。
可以看到是1,所以返回回来的是字符型。也就是函数要求的返回类型。
最后我们来看看第5点,其实也很简单,你想想,如果一个要求要返回内容的函数却没有返回内容会怎么办,肯定会出现问题啊,可能你十分肯定不会有其他情况发生,但是万一呢,万一发生了呢,可能你会想那再改嘛,但是给了用户后发生这种情况导致程序崩溃了,那是多大的损失啊。所以为了防止这种事情的发生,函数中如果有if,那必须都有返回值,不然程序会报错的。
二、数组做函数参数
我们在使用函数的过程之中,难免会使用到数组做参数,就比如说我们如果想写一个函数,让它把数组里的数据都变成0,那就得把数组传参到函数中,那我们接下来了解了解数组如何传参吧。
我们可以先来写两个关于数组传参的函数,第一个就是把数组里的数据都变成0的函数,第二个则是打印数组的函数。
cs
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
set_arr(); //把数组里的数据都变成0
print_arr(); //打印数组的函数
return 0;
}
我们可以先来想想,如果我们要去让数组内的数据都变成0的话,肯定还是要知道数组里的元素个数的,而且我们之前传实参的时候都是在()内输入要传过去的东西的名字的,按道理来讲数组也是这样的,我们之前说过数组的名是[ ]前的,也就是arr,所以说最后就变成了这样。
cs
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sz = sizeof(arr) / sizeof(arr[0]); //求元素的个数
set_arr(arr, sz); //把数组里的数据都变成0
print_arr(arr, sz); //打印数组的函数
return 0;
}
这就是实参的写法,实参传输过去变成形参啦,那形参该怎么写呢?我们参考上一章节的内容就可以知道其实很简单,唯一困难的地方就是不知道数组该用什么类型来表示。这里我们来复习一下我们前面讲数组是说数组是什么类型的啊,除去我们命名的东东其他的就是它的类型,比如我们这里的arr它的类型是int [ ],那我们的形参是不是 int [ ] arr呢,答案肯定是否定的,其实没有那么复杂,直接就是数组啥样它啥样就行了,数组是int arr[ ],那形参也是int arr[ ];当然,如果你想换个名字,比如说int brr[ ]也可以。所以说最终函数长这样。
cs
void set_arr(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
arr[i] = 0;
}
}
void print_arr(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sz = sizeof(arr) / sizeof(arr[0]); //求元素的个数
set_arr(arr, sz); //把数组里的数据都变成0
print_arr(arr, sz); //打印数组的函数
return 0;
}
这就是数组的传参方式,当然,不知这一种,等我们学到指针时还有其他的办法会更实用一点。当然,数组这里还有几个重要的知识点得掌握。
- 函数的形式参数要和函数的实际参数相匹配。
也就是说实参传了两个参数过去,那形参就一定要用两个参数接收,不能多也不能少。
-
函数的实参是数组,形参也是可以写成数组的形式的。
-
形参如果是一维数组,那数组的大小可以省略不写。
也就和我上面的相同,[ ]中没有数组的大小,当然如果写了也无伤大雅。
-
形参如果是二维数组,行可以省略,但是列不行,就和创建二维数组相同。
-
数组传参,形参是不会创建新数组的。
也就是说实参的数组和形参的数组是一个数组,而不是和上节课那样只是数据相同,但不是同一个东西,数组就是同一个数组。
- 形参操作的数组和实参操作的数组是同一个数组。
同上面同理。
三、嵌套调用和链式访问
3.1 嵌套调用
嵌套调用就是函数之间相互调用,就像孔明锁或者鲁班锁,各种不同的结构相互平凑在一起,形成了一个非常稳固的结构。而正是因为函数之间有效的相互调用才形成了相对的的程序。
假如我们要写一个程序,要让它求出某年某月有多少天怎么做呢?我们来看一下。
cs
int main()
{
int years = 0;
int month = 0;
scanf("%d %d", &years, &month);
int day = get_month_day(years, month); // 用来求某年某月有多少天的函数
printf("%d", day);
return 0;
}
这是主函数,我们可以先创建一个函数来解决这个问题,我们来分析一下这个问题,其实很简单,月份基本上是固定的,唯一一个不固定的月份就是二月,闰年二月29天,平年二月28天,所以问题就变成了一个找闰年的问题,我们可以再创建一个函数来判断是不是闰年。
cs
int is_leap_year(int y) //是闰年返回1,不是返回0
{
if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0)
{
return 1;
}
else
{
return 0;
}
}
int get_month_day(int y, int m)
{
int arr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//由于arr的下标是从0开始的,所以说我们可以空一位,让下标0变成0
if (is_leap_year(y) == 1 && m == 2)
{
return arr[m] + 1;
}
else
{
return arr[m];
}
}
int main()
{
int years = 0;
int month = 0;
scanf("%d %d", &years, &month);
int day = get_month_day(years, month); // 用来求某年某月有多少天的函数
printf("%d", day);
return 0;
}
我们可以得到以上的函数,这就能很好的满足我们的要求,这就是嵌套调用。
当然还可以用switch,大家可以去试一试,这里就不带大家尝试了。
大家要注意函数不能嵌套定义,就是说在一个函数中在定义一个函数是万分不可的。
3.2 链式访问
链式访问就是值把一个函数的返回值作为一个函数的参数,像链条一样将函数串起来就是链式访问。
就比如我们之前玩过的strlen函数,它的作用是求字符串长度
cs
int main()
{
int a = strlen("ABCDEFG");
printf("%d\n", a);
return 0;
}
这个代码很容易懂,就是求出ABCDEFG的大小后存储在a中,然后再用printf函数输出出来。
可能有人就在想了,能不能直接把strlen写在printf里面,不用a保存,比如
cs
int main()
{
printf("%d\n", strlen("ABCDEFG"));
return 0;
}
当然可以,这就是一个链式访问,将strlen函数的返回值放在printf的参数之中。
四、函数声明和定义
4.1 单个文件
我们在创建函数的时候一般都是把函数写在主程序的前面,例如
cs
int Add(int a, int b) //函数的定义
{
return a + b;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = Add(a, b);//函数的调用
printf("%d", c);
return 0;
}
我们把上面的函数叫做函数的定义,而下面引用函数的地方叫做函数的调用。但是我们其实也可以把函数写在主函数的下面。
cs
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = Add(a, b);//函数的调用
printf("%d", c);
return 0;
}
int Add(int a, int b) //函数的定义
{
return a + b;
}
这个时候我们运行代码会发现程序报错了。
上面显示说是Add未定义,原因是因为编译器在从上往下编译时发现主程序中有Add这个函数,但是它在前面没有看见过,所以就显示未定义,这个时候我们就可以声明一下。
cs
int Add(int a, int b);//函数的声明
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = Add(a, b);//函数的调用
printf("%d", c);
return 0;
}
int Add(int a, int b) //函数的定义
{
return a + b;
}
这就是函数的声明,这样程序就能正常运行了,当然,函数的定义是特殊的函数声明。
4.2 多个文件
其实我们一般在创建函数是都不会在一个文件中创建,一般会分为两个文件
.h(头文件) ---- 函数的声明;
.c(源文件) ---- 函数的定义;
以上就是我们声明函数的地方。
以上就是我们函数定义的地方。
我们在头文件中声明函数,在源文件中定义函数,最后再在我们的主文件中使用函数,这就是多个文件的函数声明和定义。
声明:
定义:
使用:
在使用时要注意要引用我们自己创建的头文件,我们自己的头文件用" "来引用。
总结
以上就是函数的内容啦,希望大家好好吸收和理解下一章节我来说说操作符吧。感谢大家的观看,如果哪里有误,欢迎支持,谢谢大家。