C语言基础——函数(2)

ʕ • ᴥ • ʔ

づ♡ど

🎉 欢迎点赞支持🎉

文章目录

前言

一、return语句

二、数组做函数参数

三、嵌套调用和链式访问

[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这么重要,那它到底怎么使用呢,我们来了解了解。

  1. return后面可以是一个数值,也可以是一个表达式,但是如果return后面是表达式的话要先计算表达式的内容,在返回结果。

  2. return后面可以什么都没有,直接写return,这种写法适合函数返回类型是void(无类型返回)的情况

  3. return返回的值和函数返回的类型不一致,系统会自动返回的值转换成函数返回的类型。

  4. return执行后,函数就彻底返回,后面的代码不执行。

  5. 如果函数中出现了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;
}

这就是数组的传参方式,当然,不知这一种,等我们学到指针时还有其他的办法会更实用一点。当然,数组这里还有几个重要的知识点得掌握。

  1. 函数的形式参数要和函数的实际参数相匹配。

也就是说实参传了两个参数过去,那形参就一定要用两个参数接收,不能多也不能少。

  1. 函数的实参是数组,形参也是可以写成数组的形式的。

  2. 形参如果是一维数组,那数组的大小可以省略不写。

也就和我上面的相同,[ ]中没有数组的大小,当然如果写了也无伤大雅。

  1. 形参如果是二维数组,行可以省略,但是列不行,就和创建二维数组相同。

  2. 数组传参,形参是不会创建新数组的。

也就是说实参的数组和形参的数组是一个数组,而不是和上节课那样只是数据相同,但不是同一个东西,数组就是同一个数组。

  1. 形参操作的数组和实参操作的数组是同一个数组。

同上面同理。

三、嵌套调用和链式访问

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(源文件) ---- 函数的定义;

以上就是我们声明函数的地方。

以上就是我们函数定义的地方。

我们在头文件中声明函数,在源文件中定义函数,最后再在我们的主文件中使用函数,这就是多个文件的函数声明和定义。

声明:

定义:

使用:

在使用时要注意要引用我们自己创建的头文件,我们自己的头文件用" "来引用。


总结

以上就是函数的内容啦,希望大家好好吸收和理解下一章节我来说说操作符吧。感谢大家的观看,如果哪里有误,欢迎支持,谢谢大家。

相关推荐
lb36363636361 小时前
分享一下arr的意义(c基础)(必看)(牢记)
c语言·知识点
Swift社区3 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht3 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht3 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20243 小时前
Swift 数组
开发语言
南东山人3 小时前
一文说清:C和C++混合编程
c语言·c++
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc5 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe6 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql