一、 什么是函数?
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、 自定义函数【自己写函数】
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】 三角形算面积
必须使用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、实际参数:实参
实参:一般指调用函数时,函数名后()内的对象。
特色:在函数调用的时候,实参会把其值对位传递给形参,而且是单向值传递!【形参不会反向传给实参的!】
还有别的写法
#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!=43!
3!=32!
2!=21!
1!=1
把上述的从上往下分析的过程称之为:递推!
【推理:推理到1!=1为止!】
【推理:找到问题的求解方法,求某个数的阶乘是利用比它小的一个数的阶乘来帮助求解】
递归:
【归:回去,向上返回计算出结果】
注意:
【1】 在调用一个函数的过程中,又调用另外一个别的函数,称之为函数的嵌套调用
【2】
在调用一个函数的过程中,又调用该函数本身,称之为函数的递归调用!
构造函数的递归调用必须要有2个要素【缺1不可!】
函数的递归调用算法【自己分析出来】
终结条件
但是要注意:
写法是错的,因为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】 特点:只要是循环的代码,都可以用函数的递归调用来书写!
二, 数组作为实参:就是数组名!
对于数组作为参数来说好处:
【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;
}
}
}
它中的所有参数以及内部 局部变量,当该函数没有被调用的时候是系统不会给它们划分内存空间的,但该函数一定被调用,系统将会立即在内存中划分它们各自所需要的内存空间!
【一旦该函数被执行结束后,它们自己必须做到刚所分配的内存空间自动释放!】