C语言函数

函数

一、 什么是函数?
y=f(x),f(x)=2*x+1
如何称呼f的? ① 对应法则【映射】
② 其实就是函数
数学中
y=sinx
y=sin(x)

== 如果要实现某个具体的功能,所书写的对象,称之为函数!==
二、 C语言中函数的分类
1、 main函数:主函数
2、 库函数:“仓库”里面的函数
“仓库”指的是C语言中各种头文件
这些头文件如同仓库一样,里面装了很多函数
stdio.h scanf,printf,getchar,putchar,gets,puts等
math.h pow,fabs等
windows.h system等
string.h strlen等
这里面的函数都是定义好的函数,大家需要会用即可,不管具体如何实现的问题。
3、 自定义函数【自己写函数】
C语言函数_第1张图片
1、函数名
2、函数名后(),()放的参数
3、功能代码,{}内书写的
4、有无返回值的问题
void 无返回值
四、 一个自定义函数编写的步骤
例1, 任意输入2个数值求最大值

#include
int main() {
	double a, b, big;

	printf("输入一个数:");
	scanf("%lf", &a);
	printf("输入一个数:");
	scanf("%lf", &b);

	if (a > b) {
		big = a;
	}
	else {
		big = b;
	}
	printf("最大值是%lf\n", big);
	return 0;
}

要求:将对任意2个数求最大值的功能书写为自定义函数
步骤1:给自定义函数命名,例如这里max
步骤2:函数名后(),
先理解参数的意思:如果要完成某个功能是否需要已知XXX数据?
分需要和不需要!
()为空,无参数:不需要已知任何数据

()不为空,有参数:需要已知XXXX数据
所以上述图片fact函数必须属于有参数的函数
【1】 参数的个数:fact函数就是1个参数
【2】 参数的类型:int ,double,char这类
严格要求:如果参数类型一样,也必须分开书写!不能共用!
步骤3:直接功能代码,该咋写就咋写!
步骤4:考虑有无返回值!
① 有返回值: 前提功能代码最后必须有数据结果!
接下来数据结果是否需要另做他用?需要另做他用!
【别处还在存在使用该功能计算出的结果!】

例如:sin(x)你们觉得是否存在另做他用?
举例:有的,证明
【1】 tanx=sinx/cosx
【2】 三角形算面积
C语言函数_第2张图片
必须使用return语句实现返回数据结果!
return用法
return 对象;
return(对象);
严格规定:对象有且仅有一个!只能返回一个值!【不能返回多个值】
建议:如果做了return,建议别把数据结果输出!
写了return之后,最后必须在自定义函数名字前加返回对象的数据类型名!
② 无返回值
【1】有数据结果,但是你个人觉得不需要另做他用!
建议:直接在自定义函数内部把数据结果输出吧!
都已经定了:不需要另做他用!那就不返回呗
最后必须在自定义函数名前加void:无返回值的!
【一般有数据结果的自定义建议大家别输出结果,做返回值】
【2】没有数据结果,根本就没有机会去做另做他用啊!
智能选择无返回值!

#include
double max(double a, double b) {
	double big;
	if (a > b) {
		big = a;
	}
	else {
		big = b;
	}
	return big;
}
int main() {
	double a, b, big;

	printf("输入一个数:");
	scanf("%lf", &a);
	printf("输入一个数:");
	scanf("%lf", &b);

	big = max(a, b);
	printf("最大值是%lf\n", big);
	return 0;
}

五、 函数调用写法
1、 经典写法【有返回值对象】
例如:big=max(a,b);
2、【无返回值的函数调用写法】
例如:max(a,b);
3、函数的嵌套调用:【必须是有返回值的函数】
big=max(max(a,b),c);

六、参数的分类
1、形式参数:形参

double max(double a, double b) {
	double big;
	if (a > b) {
		big = a;
	}
	else {
		big = b;
	}
	return big;
}

上面double a, double b是形参。
上面max(a,b)中的a,b是实参。
形参:一般指自定义函数名后()内的对象
如果它所属的函数没有被调用的时候,是不会执行
【不会被划分存储空间的】
它只有被调用的时候,才会有系统给它划分存储空间
并且一旦它所属的自定义函数执行结束后,必须做到占用的内存空间自动释放!
2、实际参数:实参
实参:一般指调用函数时,函数名后()内的对象。
特色:在函数调用的时候,实参会把其值对位传递给形参,而且是单向值传递!【形参不会反向传给实参的!】
C语言函数_第3张图片
还有别的写法

#include

int main() {
	double a, b, big;

	printf("输入一个数:");
	scanf("%lf", &a);
	printf("输入一个数:");
	scanf("%lf", &b);

	big = max(a, b);
	printf("最大值是%lf\n", big);
	return 0;
}
//函数的定义
double max(double a, double b) {
	double big;
	if (a > b) {
		big = a;
	}
	else {
		big = b;
	}
	return big;
}

上述代码报错,提示错误:max函数没有定义【因为你没有先定义再使用!】
如何解决1:把自定义函数放在main函数上面【符合先定义再使用!】
如何解决2:做一个自定义函数的声明!

七、 函数的声明

理解:什么叫声明?

告知!没有太多实际的操作而已!

例如这里告知主函数你下面有一个自定义函数max嘛
如何告知?告知什么?
double max(double a,double b)
包含了一个自定义的三个要素【三部分!】
做法:把函数的首部【自定义函数的首行直接复制粘贴到main函数中第一行去即可】
如果自定义函数在前,调动它的函数在后,可以不用做函数声明!

#include
double max(double a, double b);//这就是函数声明
int main() {
	double a, b, big;

	printf("输入一个数:");
	scanf("%lf", &a);
	printf("输入一个数:");
	scanf("%lf", &b);

	big = max(a, b);
	printf("最大值是%lf\n", big);
	return 0;
}
//函数的定义
double max(double a, double b) {
	double big;
	if (a > b) {
		big = a;
	}
	else {
		big = b;
	}
	return big;
}

例2 任意输入3个数值求最大值
1:直接修改max函数!可以的!【建议不采纳方案】
2:前提函数的最大优点是什么?
函数可以反复,多次,不间断的随便调用!

#include
double max(double a, double b);//这就是函数声明
int main() {
	double a, b,c,big;

	printf("输入一个数:");
	scanf("%lf", &a);
	printf("输入一个数:");
	scanf("%lf", &b);
	printf("输入一个数:");
	scanf("%lf", &c);

	big = max(a, b);
	big=max(big, c);//多次调用
	printf("最大值是%lf\n", big);
	return 0;
}
//函数的定义
double max(double a, double b) {
	double big;
	if (a > b) {
		big = a;
	}
	else {
		big = b;
	}
	return big;
}

写个自定义函数,我可以用无数次!
好处1:减少了代码的书写!
好处2:功能独立!独立成一个自定义函数了!

#include
double max(double a, double b);//这就是函数声明
int main() {
	double a, b,c,big;

	printf("输入一个数:");
	scanf("%lf", &a);
	printf("输入一个数:");
	scanf("%lf", &b);
	printf("输入一个数:");
	scanf("%lf", &c);

	big = max(max(a, b), c);//函数的嵌套调用
	printf("最大值是%lf\n", big);
	return 0;
}
//函数的定义
double max(double a, double b) {
	double big;
	if (a > b) {
		big = a;
	}
	else {
		big = b;
	}
	return big;
}

最后一个版本
建议自定义函数如果有结果还是做返回吧
关于main函数,最后做一个return
解释: 为什么main函数建议最后写一个return 0;
返回的0是给计算机系统!
告知计算机系统我的这个程序全部执行结束了!
如果以后写自定义函数版本的程序。
【1】 写一个纯main版本的程序,验证功能是否正确。
【2】 然后再改写为自定义函数版本:抠!
准备新的写法:功能独立
【1】 功能独立:独立成了自定义函数
【2】 功能独立:自定义函数独立成另外一个文件【放在自己的头文件中】!
前提:math.h它是一个头文件,它里面包含了很多数学函数pow,fabs等函数

#include
#include"源1.h"
//原本这里是max函数:把他抠走,放源1头文件中
//所以必须在max函数本来的位置把max函数做include包含
int main() {
	double a, b, c, big;

	printf("输入一个数:");
	scanf_s("%lf", &a);
	printf("输入一个数:");
	scanf_s("%lf", &b);
	printf("输入一个数:");
	scanf_s("%lf", &c);

	big = max(max(a, b), c);//函数的嵌套调用
	printf("最大值是%lf\n", big);
	return 0;
}

注意:现在的自定义函数的“独立”,不仅仅是独立成自定义函数,还把自定义函数独立到自己写的头文件中!!!
优点:可以提高代码的重复使用率!【直接复制头文件到其他程序中,注意一定要和源文件放在一起】
如果没有放在一起?必须书写绝对路径!

头文件的引用写法
#include
#include"D:\jiecheng.h"
区别在于
【1】#include<…….h>
【2】#include”…….h”

在vc++6.0,vs2022,devcpp安装中有一个文件夹,文件名字就是“include”
这个文件夹中有很多的.h的头文件,都是系统定义好的头文件。
【1】 所以如果要去引用这些头文件,并且使用其中的函数,那就使用#include<…….h>
<>的方式它直接到安装路径下的include文件夹中去寻找所需<>内的头文件
【2】 “”先到源文件.c文件所属的文件夹中去寻找所需要的头文件,如果存在,则直接把其包含到代码中来使用。但是如果不存在的话,再到到安装路径下的include文件夹中去寻找所需””内的头文件,如果存在的话,就直接包含到代码中来。

例3 任意输入一个正整数给n,求n!
分析:
例如先准备分析求5!

5!=12345
处理一下
5!=54321
再处理一下
5!=54!
4!=4
3!
3!=32!
2!=2
1!
1!=1
把上述的从上往下分析的过程称之为:递推!
【推理:推理到1!=1为止!】
【推理:找到问题的求解方法,求某个数的阶乘是利用比它小的一个数的阶乘来帮助求解】
C语言函数_第4张图片
递归:
【归:回去,向上返回计算出结果】
C语言函数_第5张图片
注意:
【1】 在调用一个函数的过程中,又调用另外一个别的函数,称之为函数的嵌套调用
【2】 在这里插入图片描述
在调用一个函数的过程中,又调用该函数本身,称之为函数的递归调用!
构造函数的递归调用必须要有2个要素【缺1不可!】
C语言函数_第6张图片

函数的递归调用算法【自己分析出来】
C语言函数_第7张图片
终结条件
但是要注意:
C语言函数_第8张图片
写法是错的,因为C语言对=,也就是赋值运算符有一个严格的规定!其左边必须是自变量!
左边fact(n)是自变量吗?
不是!所以必须换自变量写法!

#include"stdio.h"

int fact(int n) {
	int s, i;
	//s=1*2*3;
	if (n >= 2) {
		s = n * fact(n - 1);
	}
	else if (n == 1) {
		s = 1;
		//每个s包含了1!2!···
		//每个数的阶乘的结果都是用来算另一个数
		//不能输出s,必须返回
		return s;
	}
}
int main(){
	int n, s;
	printf("输入一个正整数:");
	scanf("%lld", &n);
	s = fact(n);
	return 0;
}

蓝桥杯竞赛题目
“李白街上走,提壶去买酒,遇店加一倍,见花喝一斗”,途中,遇见5次店,见了10此花,壶中原有2斗酒,最后刚好喝完酒,要求最后遇见的是花,求可能的情况有多少种?
分析:
如何区分遇见花和店?遇见花用0表示,遇见店用1表示。
第1次遇见的是?[0,1]
第2次遇见的是?[0,1]
……
第15次遇见的是?[0,1]
0到1是范围,涉及到范围用for来书写!
数组元素当变量来用!
方法1:暴力破解法,枚举法

#include
int main()
{
	int jiu,hua,dian,s,i;
	int a[16];
	//15个for循环 a[1] a[2] ……a[15]
	//数组元素的下标1-15刚好和第1次到第15次挂钩
	s=0;
	for(a[1]=0;a[1]<=1;a[1]++)	 
	for(a[2]=0;a[2]<=1;a[2]++)
	for(a[3]=0;a[3]<=1;a[3]++)
	for(a[4]=0;a[4]<=1;a[4]++)
	for(a[5]=0;a[5]<=1;a[5]++)
	for(a[6]=0;a[6]<=1;a[6]++)
	for(a[7]=0;a[7]<=1;a[7]++)
	for(a[8]=0;a[8]<=1;a[8]++)
	for(a[9]=0;a[9]<=1;a[9]++)
	for(a[10]=0;a[10]<=1;a[10]++)
	for(a[11]=0;a[11]<=1;a[11]++)
	for(a[12]=0;a[12]<=1;a[12]++)
	for(a[13]=0;a[13]<=1;a[13]++)
	for(a[14]=0;a[14]<=1;a[14]++)
	for(a[15]=0;a[15]<=1;a[15]++)
	{
		jiu=2,hua=0,dian=0;
		for(i=1;i<=15;i++) 
		{
			if(a[i]==0)//表示遇见的花
				jiu--,hua++;
			else if(a[i]==1)//遇见的是店
				jiu=jiu*2,dian++;
		}
		//遇见5次店,见了10此花,壶中原有2斗酒
		//最后刚好喝完酒,要求最后遇见的是花
		if(dian==5 && hua==10 && a[15]==0 && jiu==0)
		{
			s++;
			for(i=1;i<=15;i++)	
				printf("%d",a[i]);
			printf("\n");
		}	
	}
	printf("%d\n",s);
	return 0;
} 

上述代码的缺点:代码好长!
因为15次遇见,所以是15个for!
如果是1000次遇见呢?1000个for!
方法2:
每1次遇见的处理要么花,要么是店
每次遇见的处理都是类似的!
函数的递归调用:如果在调用一个函数的过程中,又调用该函数本身
【对任何数值的求解方法是一样的!】
fact(5)算5的阶乘,和fact(50)算50的阶乘方法一样吗?
一样!
从点出发来看的话,很明显可以用函数递归调用来书写!

最终函数的递归调用写法如下

#include"stdio.h"
int a[16];//依次存放15次遇见的情况
int s = 0;//统计题目的组合数量
void libai(int hua, int dian, int jiu, int i) {
	if (i == 16) //终结条件 
	{
		if (hua == 10 && dian == 5 && jiu == 0 && a[15] == 0)
		{
			int j;
			s++;//
			for (j = 1; j <= 15; j++)
				printf("%d", a[j]);
			printf("\n");
		}
	}
		else//递归算法,15次遇见还没有做完,还在遇见中
		{
			//说明还在第i次遇见中
			//先讨论遇见花
			a[i] = 0;
			//继续下一次遇见
			libai(hua + 1, dian, jiu * 2, i + 1);
			//再讨论店
			a[i] = 1;
			//继续下一次遇见
			libai(hua,dian + 1,jiu * 2, i + 1);
		}
}


int main() {
	libai(0, 0, 2, 1);//开始,遇见花0次,遇见店0次,酒2斗
	printf("s=%d\n", s);
	return 0;
}

函数的递归调用的本质上是循环
【1】 优点:代码短
【2】 特点:只要是循环的代码,都可以用函数的递归调用来书写!

二, 数组作为实参:就是数组名!
C语言函数_第9张图片
对于数组作为参数来说好处:
【1】 如果要把某个函数中多数值代入到另外一个函数中,建议使用数组作为自定义函数的参数
【2】 如果要把某个函数中的多个结果返回给另外一个函数中,建议不使用return【因为用不了】,还是选择用数组作为形参,这样的不需要写return也能间接带回多个数值到调用函数内部!
【3】 为什么?原因是因为数组作为参数的时候,实参传递给形参的不是数组所有的值,而是传递的是地址过去!地址一样的话,意味着2个数组访问的是同一段内容空间!

例 输入n个数值,进行升序的排序,要求排序功能用自定义函数来实现!
方法1:非自定义函数版本

#include"stdio.h"
int main() {
	double h[1000], t;
	int i, j, n;

	printf("输入人数n:");
	scanf("%d", &n);

	printf("输入%d个人的身高如下\n", n);
	for (i = 0; i <= n - 1; i++) 
		scanf("%lf", &h[i]);

	for(j=0;j<=n-1;j++)
		for (i = 0; i <= n - 2 - j; i++) {
			if (h[i] > h[i + 1]) {
				t = h[i];
				h[i] = h[i + 1];
				h[i + 1] = t;
			}
	}

	printf("输出排序后的%d个人的身高如下\n", n);
	for (i = 0; i <= n - 1; i++)
		printf("%lf\n", h[i]);
	return 0;
}

方法2:自定义函数版本【全局变量和全局数组版本】

#include"stdio.h"

double h[1000], t;
int i, j, n;

void sort() {
	for (j = 0; j <= n - 1; j++)
		for (i = 0; i <= n - 2 - j; i++) {
			if (h[i] > h[i + 1]) {
				t = h[i];
				h[i] = h[i + 1];
				h[i + 1] = t;
			}
		}
}
int main() {
	

	printf("输入人数n:");
	scanf("%d", &n);

	printf("输入%d个人的身高如下\n", n);
	for (i = 0; i <= n - 1; i++) 
		scanf("%lf", &h[i]);

	sort();

	printf("输出排序后的%d个人的身高如下\n", n);
	for (i = 0; i <= n - 1; i++)
		printf("%lf\n", h[i]);
	return 0;
}

方法3:【局部变量和局部数组版本-数组做为参数的版本】

#include"stdio.h"


void sort(double h[1000],int n) {
	int i, j,t;//局部变量
	for (j = 0; j <= n - 1; j++)
		for (i = 0; i <= n - 2 - j; i++) {
			if (h[i] > h[i + 1]) {
				t = h[i];
				h[i] = h[i + 1];
				h[i + 1] = t;
			}
		}
}
int main() {
	double h[1000];
	int i, n;

	printf("输入人数n:");
	scanf("%d", &n);

	printf("输入%d个人的身高如下\n", n);
	for (i = 0; i <= n - 1; i++) 
		scanf("%lf", &h[i]);

	sort(h,n);

	printf("输出排序后的%d个人的身高如下\n", n);
	for (i = 0; i <= n - 1; i++)
		printf("%lf\n", h[i]);
	return 0;
}

对于一个自定义函数来说
void sort(double h[1000],int n)
{
int i,j;//局部变量
for(j=0;j<=n-2;j++)
for(i=0;i<=n-2-j;i++)
{
if(h[i]>h[i+1])
{
double t;//局部变量
t=h[i];
h[i]=h[i+1];
h[i+1]=t;
}
}
}
它中的所有参数以及内部 局部变量,当该函数没有被调用的时候是系统不会给它们划分内存空间的,但该函数一定被调用,系统将会立即在内存中划分它们各自所需要的内存空间!

【一旦该函数被执行结束后,它们自己必须做到刚所分配的内存空间自动释放!】

你可能感兴趣的:(c语言,开发语言)