7.1函数
7.1.1 函数是什么?
什么是程序,一个C程序就是由很多很多个源程序文件构成,而每个源程序文件都是由预处理指令 ,全局 数据声明 , 加上一个又一个函数构成;
函数就是用来实现某一个特定功能的东西;而函数名就是给这一功能去起一个名字;
函数是相互平行的概念;彼此间是等价得;不存在主次之分;但是函数不是不可以写在其他函数里面;可以;;这叫做函数的嵌套调用;不管是什么函数都是可以相互调用;若干次 ;任意位置;当然有一个函数不能被其他函数调用 这就是主函数main ;他是被操作系统直接调用;函数不能被嵌套定义;
程序在编译得过程中;只会从main 函数中开始执行;一直到main结束 ;其中遇到那个就调用哪个;当遇到他得时候 主函数不会停下来;他和被调用函数一起;同时进行处理;平行线
7.1.2 那我们有哪些函数呢 ??
无非就两种 ;一个是库函数;还有一个是用户自己定义的函数;
至于库函数就不说了;就是已经定义好了的;你只要会用就完了;
那自己定义的呢;他就是得自己写;我把它分成两类 ;
要返回一个函数值;这个叫做有参函数;
不需要输出返回值;那么叫做无参函数;
调用时候不一定需要输入值;这不重要;我可以输入值的时候返回一个数字,也可以不输入一个数值,也能返回一个数值;同理,我输不输入数值对无参也是一样受用;
具体咱们举例子;
无参函数;
#include <stdio.h> int main (){ void print_star(); void print_message(); print_star(); print_message(); print_star(); return 0; } void print_star() { printf("******************\n"); 无参函数 无返回值 } void print_message() { printf("wu mingda dashuaibi!\n"); 无参函数 无返回值 }
有参函数:
#include <stdio.h> int main(){ int max(int x,int y); int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("max=%d\n",c); return 0; } int max(int x,int y){ 有参函数 有返回值 有输入值 int z; if(x>y)z=x; else z=y; return(z); }
7.2函数定义
7.2.1 怎么定义一个函数;有参 无参函数 分别怎么定义?
定义一个函数;首先我们直接看看定义函数得形式;
举例子;
类型名 函数名 (形式参数)
{
函数体 (执行部分和声明部分)
}
咱们看看就是一般而言函数定义的话;你是不是需要一个函数的名字就是定义函数名;前面说了,其次对于函数要么属于能返回函数值得;或者不能返回函数值得;如果是前者的话;那么就是无参函数 ;如果能返回一个数值;那么可以称呼为有参函数值;同理而言对于能否返回值且返回值是什么类型得;你是不是需要定义;这就是类型名;对于没有返回函数值的函数你应该定义为void 对于能返回函数值得你应该 对其能返回函数值得类型进行一个定义;比如int long longlong float ;你应该有一个具体得说法;还有就是如果是需要输入数据进去得那种 ;你还需要定义一个形式参数;对于这个参数你还需要就是定义他的数据的类型;比如来说int a;具体说形式参数与实际参数得关系 ;这儿先不慌说太多;那就是形式参数其实只是一个可有可无的东西;但是在定义函数中这个参数名字必须给到,int x;
总的来说 对于函数 而言你需要定义如下
函数返回值类型 ;(如果没有 void )
函数名字;
形式参数类型 (形式参数);
函数体;
对于如此即使还有一个特殊得工程上常常定义一些空函数;为什么;就是比如写代码就是写到这;我知道要在这儿一串比较复杂得代码 但是把 还写不出来;不知道要写什么 ;我可以一些空函数放在这;然后就是随时就能回来用它;加以补充;而且写在这儿;也不影响程序的运行;应为他相当于一个空语句’不会对程序产生任何其他得影响;
7.2.2 那么咱们看看这个空函数怎么去写;
写在这儿 直接 int main(){ ...... _gaidengshuxue wumingda(); ....... } void gaidengshuxue wumingda(){ } 这就是一个空语句;你只带怎么用就行了;怎么写; 这就是函数得定义;
7.2.3 区分一下#include<> 与#include""的区别;
#include<>直接从编译器自带的函数库中寻找文件
#include""是先从自定义的文件中找 ,如果找不到在从函数库中寻找文件
如果是自己写的头文件 建议使用#include“”
< >引用的是编译器的类库路径里面的头文件
" "引用的是你程序目录的相对路径中的头文件
如果使用" ",它是会先在你项目的当前目录查找是否有对应头文件
如果没有,它还是会在对应的引用目录里面查找对应的头文件
意思就是,使用#include "stdio.h"如果你项目目录里面,没有stdio.h这个头文件,它还是会定位到库路径里的头文件
stdio.h 等头文件中包含了函数定义和申明;
7.3 调用函数
7.3.1 函数调用的形式
调用的时候就是函数名字加上函数的实参;像那种无参数就空着;具体形式;
函数名 (实参表列)
这就是调用的基本格式;完了就是调用这种函数具体用在代码中分为三种情况;
首先,就是函数调用语句;就是在基本的函数调用的一般格式的后面加上一个分号即可;
for example : age(); age(1,4);
其次就是,函数表达式 ;因为对于大部分的函数而言返回的都是一个有实际的值;而这个值往往可以放在表达式中;构成函数表达式;
举个例子:c=max(a,b);
最后就是;刚才说过有参函数调用后相当于一个数值;那么这个数值是可以放在函数中再次当作参数使用的;所以第三种就是** 函数参数**;
m=max(a,max(9,0));
7.3.2 形式参数 实际参数;
在调用函数时侯 分为形式参数 和 实际参数;形式参数就是你定义的函数括号里面的参数 ;这个在定义的时候是可以有可以没有的 ;这只是形式;只是一个临时的东西;而实际参数而言;他是主函数里面确实存在的;他放在调用的时候的括号里面;它可以是表达式 ;数值 ;函数参数等可以表示一个参数的任何表现形式;
age(a,b); int age(int x,int y){ ... }
这里面 a.b 就是实际参数 ;x,y就是 形式参数;
如果实际参数和形式参数定义的类型不同该怎么办;那就是先进行数据转换来;前面学过的;
7.3.3 函数调用的过程
函数调用这怎样处理的 ;首先实参 他是有储存单元的;max(a,b);在这里面 a,b都有自己的储存单元;同时 对于形式参数 ;int max(int x,int y)而言 x, y 也是有储存单元的 不过吧 他是临时的 就是他仅仅就是一个 用完自动消散的一个储存单元; 具体的执行过程 ;首先 就是实参储存单元的 数值直接给形式参数 ;在定义的函数体中 形式参数作为一种数值去进行各种处理 操作 数值的变化 ;但无论怎么变化 都不会影响实际参数的值 ;最后执行完结之后 ;调用函数会返回一个数值出来; 这是调用函数在这一次的使命就结束了;那么同理而言;形式参数作为一个单独的储存单元的使命就结束了;那么这个储存单元就临时解散了;
同时这里说了 调用函数会返回一个值; 要用到 return ;;;如果不返回呢 那么这里面就不能用return ;
==??==这里面有一个问题没有解决
c=isdd(a,a+b);这里面a+b作为实参那么他是单独又成为一个储存单元么;
首先 a+b 不能代表变量 ;只是一个表达式 其意义就是数值;而这个数值是放在参数的单独储存空间的;形参有一个 定义的么 实参的话 就是单独创造出来的;也即是说表列是单独有自己的储存空间的;
7.3.4 函数的返回值
作为一个调用函数函数是需要返回值的 但是返回值他需要return 来做返回 ;具体就是
return (z);
return后面的括号可以不要; 比如return 0;还有就是 z可以是表达式 或者其他形式;这样的化还是带上()比较好;函数返回值的类型是你事先定义好的; 必须最后输出的结果必须是这个类型的返回值;但是把在执行体中 的z ,他是不一定非要是这个类型的返回值 了也可以是其他的;这取决与上面对形参的定义 以及 执行体中的具体过程;那你坑定会问 如果不一样了怎么办;那么系统会强制转换就行;
7.3.5 对调用函数的声明
对调用函数的声明是必不可少的;用为编译的时候你遇到调用某个函数的时候;你会发现系统根本不知道那个函数是什么;这就会出现error 所以要事先声明;那么既然声明那么就是让电脑知道你;所以声明要包括 类型名 函数名 参数类型 参数名 参数的数量 ;都要清楚 ;就相当于一个没有执行体的定义过程;当然这里面函数名字可以省略;
函数类型 函数名(参数类型1 参数名1,函数类型2 参数名2…);
函数类型 函数名(参数类型1 ,函数类型2 …);
就这两种;;
7.3.6 函数的嵌套调用;
之前说了 ,函数是可以嵌套调用的; 但是不能够相互间嵌套定义;
怎么嵌套调用 看一个实际例子就可以了;
#include <stdio.h> int main (){ int max4(int a,int b,int c,int d); int a,b,c,d,max; printf("please enter 4 number:"); scanf("%d %d %d %d",&a,&b,&c,&d); max=max4(a,b,c,d); printf("max=%d\n",max); return 0; } int max4(int a,int b,int c,int d) { int max2(int a,int b); return (max2(max2(max2(a,b),c),d)); } int max2(int a,int b){ return (a>=b?a:b); } /* int max4(int a,int b,int c,int d) { int max2(int a,int b); int m; m=max2(a,b); m=max2(m,c); m=max2(m,d); return (m); } int max2(int a,int b){ return (a>=b?a:b); } */
从这段代码可以看出;函数的嵌套;就是像上面这样使用的;多用就完了
7.3.7递归调用以及例题论证
函数的递归调用;咱们首先来看看咱们书本上面的题目;**直接调用或者间接调用函数本身叫做递归调用;**咱们看看 ;具体的例子;
/*这是一道求n!的题目*/ #include <stdio.> int main(){ int fac(int n); int n; int y; printf("input an inputer mnumber:"); scanf("%d",&n); y=fac(n) ; printf("%d!=%d\n",n,y); return 0; } int fac(int n){ int f; if(n<0) printf("数据错误;"); else if(n==o||n==1) f=1; else f=fac(n-1)*n; return(f); }
#include <stdio.h> int main(){ int age(int n); printf("no.5.age:%d\n",age(5)); reyurn(0); } int age(int n){ int c; if(n==1) c=10; else c=age(n-1)+2; return (c); }
就是这两道题目;然后咱们看看就是;什么是函数的·递归调用就是 说 咱们 就是首先函数当调用自己的 那么就是会出现这个情况 ;执行这个函数 ;遇到调用;原来是自己;调用自己;又遇到自己;在调用自己;如此反复;重复就是;出现了一个又一个的死循环;那么需要有一个能结束死循环的东西;所以就出现了 ;**if else 语句 来终止循环 ;**还有就是函数递归调用其实本质就是函数的回溯 递推 的过程;所谓回溯就是反推 一直推到终止条件;即使解决死循环的key ;有了这个Key 你就可返回来一步步求出具体值;那这个过程就是递推;;以上你可以看出;
求n! 先是age(n-1)*n 然后要求age(n-1)=age(n-2)*(n-1) ......... 回溯到了 age(1)=1; 然后一步步倒回去; 得出age(2) ...... age(n-1) age(n); 就是结束;
自己调用自己 就是函数递归调用;解决办法就是函数回溯 递推;
再看一道例题
??例三
#include <stdio.h> int main(){ void hanoi(int n,char one,char two,char three); int m; printf("input the number of diskes:"); scanf("%d",&m); printf("the step to move %d diskes :\n",m); hanoi(m,'A','B','C'); } void hanoi (int n,char one,char two,char three){ void move(char x,char y); if(n==1) move(one,three); else{ hanoi(n-1,one,three,two); move(one,three); hanoi(n-1,two,one,three); } } void move(char x,char y){ printf("%c--->%c\n",x,y); }
7.4 数组类做函数参数
7.4.1 数组元素作为函数实参
咱们看形参只是一个临时变量;他是在函数调用完后自行解除的;所以只能是实参来提供,而不能直接赋值;函数是可能被调用多次的;所以说只能形式参数可以是任意变量提供的数值;所以就是所以不能直接赋值实参,那么他就没有绝对的自由;只能是某一个储存空间数值的改变;如果是这样他是不合规矩的;他应该是任意储存空间都能提供数值;那么就是说;只能通过实参转化形参;
那那些可以作为实参呢;常量;变量;??表达式;数组;数组元素;…都可以做函数的实参;
那么我是不是可以理解为实参本身提供一个数据空间;那么常量;表达式;作为熟知的结果储存;实参的储存空间是一直不变的;而形式参数是用完就取消的;他是通过实参传递的;
这里我们讲一讲 数组元素 作为实参函数
/*输入10个数,要求输出其中最大的数以及这是第几个数字*/ #include <stdio.h> int main(){ int max(int x,int y); int a[10],m,n,i; printf("enter 10 integer numbers:"); for(i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); for(i=1,m=a[0],n=0;i<10;i++){ if (max(m,a[i])>m){ m=max(m,a[i]); n=i; } } printf("the largest number is %d\nit is the %dth number.\n",m,n+1); } int max(int x,int y){ return (x>y?x:y); }
从这串代码中我们可以看出数组元素就是某个数值;所以不用担心;它就是数值的赋值;
7.4.2 数组名 作函数参数(实参 形参)
先观察这道题目,仔细看看实参和形参位置的区别;
/*一维数组score 内放置十个成绩 求平均成绩*/ #include <stdio.h> int main() { float average(float array[10]); float score[10],aver; int i; printf("input 10 scores:\n"); for (i=0;i<10;i++) scanf("%f",&score[i]); printf("\n"); aver=average(score); printf("average score is %5.2f\n",aver); return 0; } float average(float array[10]) {int j; float ever,sum=array[0]; for(j=1;j<10;j++) sum=sum+array[j]; ever=sum/10; return(ever); }
你会发现实参和形参都是数组;而实参那儿是数组名字;仔细研究是你会发现主函数定义的数组名字不一样;但是类型一样的;我来解释一下,这是为什么;
就是首先;;之前说了实参和形参的关系 ;就是函数实参是有一个单独的储存空间的,是为了给形参赋值而存在的;所以实参里面是一个空间;;因为我只要满足让形参知道这个数组的开头就行;所以实参是数组名字;就是给形参一个地址,储存的开头的地址;所以不需要是score[10],这样首先变成了第十一个元素的地址;不行;;反正不能出现[];;只要函数名字就行;只要地址就ok;而形参,他是需要定义一个全新的数组 ;应为他和原数组就是两个;但是他们的储存空间是同一个;也就是说他和变量还是不一样的;变量的形参是给予一个临时的储存空间;他是实参把地址给过来,他们是一个地址,自然是同一个储存空间;同理,形参数值改变,原本数组的数值也改变;
所以就是说类型就要给到;而且类型最好相同;但是来说数组数量可以有 可以没有;因为编译系统不检查形参的数组大小的;可以想象成要多大有多大;任意的;不限定大小;应为会有不同数组进来嘛;所以就是说只是实参吧第一位给了形参第一位 这样形参和原数组第一位读取后是等价的;第n位置也是相互等价的;
总而言之怎么用呢;
实参数组名就行;
形参类型名要定义;目前类型要一样(也会出现不一样的也许还没遇到);
形参的数组名不能和原数组一样;否则出现错误;
后面[]要有;可以不给数值;最好不给;、
咱们再看看一个例子
/*两个班级 35人 30人 调用average 分别求平均成绩*/ #include <stdio.h> int main() { float average(float array[],int q); float score1[3]={1}; float score2[5]={1}; float aver; int i; printf("input 10 scores:\n"); for (i=0;i<3;i++) scanf("%f",&score1[i]); printf("\n"); aver=average(score1,3); printf("average score is %5.2f\n",aver); for (i=0;i<5;i++) scanf("%f",&score2[i]); printf("\n"); aver=average(score2,5); printf("average score is %5.2f\n",aver); return 0; } float average(float array[],int q) {int j; float ever,sum=array[0]; for(j=1;j<q;j++) sum=sum+array[j]; ever=sum/q; return(ever); }
再看一个例子:
/*用选择法对数组中十个数由小到大排列*/ #include <stdio.h> int main() {void sort(int array[],int n); int a[10],i; printf("enter array:\n"); for(i=0;i<10;i++) scanf("%d",&sa[i]); sort(a,10); printf("the sorted array:\n"); for(i=0;i<10;i++) {printf("%d",a[1]); printf("\n"); } return 0; } void sort(int array[],int n) {int i,j,k,t; for(i=0;i<n-1;i++) {k=i; for(j=i+1;j<n;j++) if (array[j]<array[k]) k=j; t=array[k];array[k]=array[i];array[i]=t; } }
看这个例子是选择法来对数组中是个数进行排列;咱们看一看 这里面定义的函数有依旧符和上面的逻辑;这里面是形参的数组在改变数值;但是由于他们是属于一个地址 ,所以说形参的数值改变;依旧会导致原数组储存数值的改变;然后输出原数组时候是改变后的值;