560 likes | 710 Views
温故知新. 函数. 1 〠C è¯è¨€æ˜¯ç”± æž„æˆçš„?. 函数. ( 是 C 程åºçš„基本å•ä½ï¼Ÿï¼‰. 2 ã€ä¸€ä¸ª C 程åºåªèƒ½ç”±ä¸€ä¸ªå‡½æ•°æž„æˆï¼Œå¹¶ä¸”这个函数是 main( ) 。. ×. 编写程åºï¼Œæ±‚ s=s1+s2+s3+s4 的值, å…¶ä¸ : s1 =1+1/2+1/3+…+1/ 50 s2 =1+1/2+1/3+…+1/ 100 s3 =1+1/2+1/3+…+1/ 150 s4 =1+1/2+1/3+…+1/ 200. #include “stdio.h†main () ï½›
E N D
温故知新 函数 1、C语言是由构成的? 函数 ( 是C程序的基本单位?) 2、一个C程序只能由一个函数构成,并且这个函数是main( ) 。 ×
编写程序,求s=s1+s2+s3+s4 的值, 其中: s1 =1+1/2+1/3+…+1/50 s2 =1+1/2+1/3+…+1/100 s3 =1+1/2+1/3+…+1/150 s4 =1+1/2+1/3+…+1/200
#include “stdio.h” main() { float s1=0,s2=0,s3=0,s4=0,sum=0; int i; for(i=1;i<=50;i++) s1+=1/(float)i; for(i=1;i<=100;i++) s2+=1/(float)i; for(i=1;i<=150;i++) s3+=1/(float)i; for(i=1;i<=200;i++) s4+=1/(float)i; sum=s1+s2+s3+s4; printf(″sum=%f″,sum); }
此题目可以首先编一函数,用于求1+1/2+1/3+…+1/n的值,此题目可以首先编一函数,用于求1+1/2+1/3+…+1/n的值, 然后通过函数调用求s的值。 #include “stdio.h” float Fcount(int n) { float s; int i; s=0; for(i=1;i<=n;i++) s+=1/(float)i; return(s); } main() { float sum; sum=Fcount(50)+Fcount(100)+Fcount(150)+Fcount(200); printf(″sum=%f″,sum); }
函数机制的优点 • 使程序变得更简短而清晰 • 有利于程序维护 • 可以提高程序开发的效率 • 提高了代码的重用性
项目五 根据条件进行学生成绩汇总 项目要求 一个班有40位学生(分成五个组,但每个组的人数不一样)参加了期终考试(考了三门课,分别是数学、语文、英语),老师想统计以下信息: 1、统计小组一门课程的总分及平均分; 2、统计小组若干门课程的总分及平均分; 3、输出排序后小组三门课成绩单。 设计一个菜单,通过输入1-3的编号进行选择完成不同的操作。
任务1 统计小组一门课程的总分及平均分 一、问题情景 一个班有40位学生(分成五个组,但每个组的人数不一样)参加了期终考试(考了三门课,分别是数学、语文、英语),请你用菜单的方式:求小组一门课程的总分及平均分。 分析:主函数的功能是设计一个菜单,由所选择的菜单调用相应的函数,但为了界面清晰,所以程序的执行过程中多次用一条线划界。 所以问题就归结为制作一条线的函数及求一门课程的总分及平均分。
相关知识 1.无参函数的定义形式:类型标识符 函数名( ) { 声明部分 语句 } int max( ) { int x=6,y=3,z; z= x>y?x:y; return z ; }
相关知识 (2)通过return语句来实现的。 实现形式: return(z); return z; return 后面可以是表达式, 如: 类型标识符号——函数值的类型 (1)函数值的类型:在定义函数时指定函数值的类型. int max( ); char letter( ); int max( ) { int x=6,y=3; return(x>y?x:y); }
相关知识 (3) 函数没有return语句时,返回的是一个不确定的值. printstar( ) { printf("***************\n"); } main( ) { int a; a=printstar( ); printf("a=%d\n",a); } 注意:如果在定义函数时不指定类型, 系统隐含指定函数为int型。
相关知识 (4)为了明确表示“不带回值”可用 “void” 定义“无类型” void printstar( ) { printf(“********\n”); }
函数说明语句 相关知识 无参函数的调用 函数类型 函数名() {函数体;} main() {语句; 函数名(); 语句;} 函数类型 函数名(); main() {语句; 函数名(); 语句;} 函数类型 函数名() {函数体;} 被调函数定义在主调函数之前时,可以不加以声明, 否则要先声明再使用
【例5-1】输出十行十列的星号(要求用函数调用方式解决)【例5-1】输出十行十列的星号(要求用函数调用方式解决) 方法二(主函数在后) #include "stdio.h" void pp() {printf("**********\n");} main() {int i; for(i=1;i<=10;i++) pp(); } 方法一(主函数在前) #include "stdio.h" void pp(); main() {int i; for(i=1;i<=10;i++) pp(); } void pp() {printf("**********\n");}
【例5-2】用菜单的形式分别选择百钱买百鸡,九九表,水仙花数【例5-2】用菜单的形式分别选择百钱买百鸡,九九表,水仙花数 /*乘法九九表*/ void jjb() {int i,j; printf("\n乘法九九表\n"); for(i=1;i<=9;i++) { for(j=1;j<=i;j++) printf("%-5d",i*j); printf("\n"); } } #include "stdio.h" void sxhs(); void jjb(); void bqmbj(); main() {int i; printf("1.水仙花数\n"); printf("2.乘法九九表\n"); printf("3.百钱买百鸡\n"); printf("请选择1~3的菜单:"); scanf("%d",&i); if (i==1)sxhs(); //当x=1,调用求水仙花数的函数 if (i==2)jjb(); if (i==3)bqmbj(); } /*水仙花数*/ void sxhs() { int i,a,b,c; printf("\n下列数字为水仙花数:\n"); for(i=100;i<=999;i++) { a=i/100; b=(i-a*100)/10; c=i-a*100-b*10; if (i==a*a*a+b*b*b+c*c*c) printf("%d \n",i); } } /*百钱买百鸡*/ void bqmbj() {int x,y,z; printf("公鸡数 母鸡数 小鸡数\n"); for(x=1;x<=50;x++) for(y=1;y<=33;y++) {z=100-x-y; if(2*x+3*y+0.5*z==100) printf(“%4d %4d %4d\n”, x,y,z);} }
相关知识 2. 有参函数的定义形式: eg: int max(int x,int y) { int z ; z=x>y ? x : y ; return(z); } 类型标识符 函数名(形式参数表列) { 声明部分 语句 }
相关知识 形式参数和实际参数 形参:在定义函数时,函数名后面括号中的变量名 实参:在调用函数时,函数名后面括号中的参数(表达式) int max (int x, int y) { int z; z=x>y? x:y ; return(z); } main( ) { int a,b,c; scanf(“%d,%d”,&a,&b); c=max(a,b); printf(“max is %d”,c); } x , y 为形式参数 a , b 为实际参数
相关知识 有参函数的调用 函数类型 函数名(形参列表) {函数体;} main() {语句; 函数名(实参列表); 语句;} 函数类型 函数名(形参列表); main() {语句; 函数名(实参列表); 语句;} 函数类型 函数名(形参列表) {函数体;} 被调函数定义在主调函数之前时,可以不加以声明, 否则要先声明再使用
【例5-10】编写一个函数,编写x!,在主函数中调用它。【例5-10】编写一个函数,编写x!,在主函数中调用它。 #include "stdio.h" int jc(int x) { int i,t=1; for(i=1;i<=x;i++) t=t*i; return t; } main( ) { int n; printf("请输入n的值"); scanf("%d",&n); printf("%d!=%d\n",n,jc(n)); }
n 【例5-3】求Cm=m!/(n!(m-n)!) #include "stdio.h" /*阶乘的函数*/ int jc(int k) {int i; int t=1; for(i=1;i<=k;i++) t=t*i; return t; } /*主函数*/ main() {int m,n,c; printf("请输入m,n的值:"); scanf("%d%d",&m,&n); c=jc(m)/(jc(n)*jc(m-n)); printf("Cmn的值为%d\n",c); } #include "stdio.h" int jc(int k); /*函数说明语句*/ main() {int m,n,c; printf("请输入m,n的值:"); scanf("%d%d",&m,&n); c=jc(m)/(jc(n)*jc(m-n)); printf("Cmn的值为%d\n",c); } /*阶乘的函数*/ int jc(int k) {int i; int t=1; for(i=1;i<=k;i++) t=t*i; return t; }
b • 2 • 3 a y x • 2 • 3 说 明 (1)实参可以是常量,变量或表达式,但必须有确定的值. max(3,a+b) (2)形参的类型必须定义,形参变量可以省略。 max(int,int); max(x,y);× (3)实参与形参的类型必须一致或赋值兼容. (4)实参同形参的数值传递是“值传递”, 由实参传给形参,不能反向,即单向传递.
3 5 3 5 x a b 传递值 y 3 temp 想一想:程序的输出结果是什么? void swap (int x, int y) { int temp; temp=x; x=y; y=temp; printf(“x=%d , y=%d \n”, x, y); } main( ) { int a=3, b=5; swap (a, b); printf(“a=%d, b=%d\n”, a, b); } x=5,y=3 a=3,b=5
任务1的具体实现 main() {int k,n,km; float sum,average; char ch; shuipx(); printf("\t班级成绩统计\n"); shuipx(); printf("1、统计小组一门课程的总分及平均分\n",n); printf("2、统计小组若干门课程的总分及平均分\n"); printf("3、输出小组排序后三门课程的成绩单\n"); printf("请输入1~3之间的一个数:"); scanf("%d",&k); shuipx(); if (k==1) {printf("请输入统计的小组的人数n="); scanf("%d",&n); shuipx(); sum=sum1(n);average=sum/n; printf("本小组的总分=%.0f\t平均分=%.1f\n",sum,average); shuipx(); } } 无参函数 #include "stdio.h" void shuipx() {printf("-------------------------------------\n"); } float sum1(int n) {int x,i; float s=0; shuipx(); printf("请输入本小组的考试成绩\n"); for(i=1;i<=n;i++) {scanf("%d",&x); s+=x;} return s; } 有参函数
练一练 1、有以下程序 fun(int a, int b) { if(a>b) return(a); else return(b); } main( ) { int x=3, y=8, r; r=fun(x,y); printf("%d\n", r); } 程序运行后的输出结果是 A)3 B)6 C)8 D)12
练一练 2、下面程序的输出是 A)1 B)2 C)8 D)10 int fun2(int x,int y) { int m=3; return(x*y-m); } main( ) { int a=7,b=5; printf(“%d\n”,fun2(a,b)/4); }
任务2 统计小组若干门课程的总分及平均分 一、问题情景 一个班有40位学生(分成五个组,但每个组的人数不一样)参加了期终考试(考了三门课,分别是数学、语文、英语),请你用菜单的方式:求小组若干门课程的总分及平均分。即完成本项目中的第二个要求。 分析: 由图可分析出主函数的功能是设计一个菜单,由所选择的菜单调用相应的函数,但为了界面清晰,所以在程序的执行过程中出现: 求小组的若干门成绩的平均分及 总分的函数又调用了一条线的函数ppp()。
函数的嵌套调用 三、相关知识 C语句不能嵌套定义函数,但可以嵌套调用函数。 也就是说在调用一个函数的过程中,又调用另一个函数。 eg:两层嵌套,其执行过程为: b函数 main 函数 a 函数 调用b函数 调用a函数 结束
b函数 main 函数 a 函数 调用b函数 调用a函数 结束 想一想:输出结果是什么? b(int x) { return(x+1); } a(int m, int n) { if(m>n) return(m); else b(5); } main() { int m=3, n=8, r; r=a(m,n); printf("%d\n", r); } 6
n 【例5-5】函数的嵌套调用。求C =m!/(n!(m-n)!)。 要求用函数的嵌套方式完成。 m #include "stdio.h" /*c的程序为*/ int jc(int k) {int i; int t=1; for(i=1;i<=k;i++) t=t*i; return t; } /*而B的程序为:*/ int cmn(int m,int n) { int z; z= jc(m)/(jc(n)*jc(m-n)); return z; } /*A的程序为:*/ main() { int m,n,c; printf("请输入m,n的值:"); scanf("%d%d",&m,&n); c=cmn(m,n); printf("Cmn的值为%d\n",c); } 这个程序就是A要调用B,而B要调用C,所以就称为函数的嵌套。
函数的递归调用 int f(int x) { int y,z; z=f(y); return(2+z); } 间接调用: int f1(int x) int f2(int t) {int y,z; {int a,c; z=f2(y);} c=f1(a);} 四、知识扩展 递归调用:在调用一个函数的过程中又出现直接 或间接的调用该函数本身的过程. 直接调用:
四、知识扩展 有5个人坐在一起,问第五个人几岁?他说比第4个人大2岁。问第4个人,他说比第3个人大2岁。问第3个人,他说比第2个人大2岁。问第2个人,他说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人是多大。 求第5个人的年龄 age(5)=age(4)+2 age(4)=age(3)+2 age(3)=age(2)+2 age(2)=age(1)+2 age(1)=10 age(n)=10 (n=1) age(n)=age(n-1)+2 (n>1)
四、知识扩展 age(int n) { int c; if(n= =1) c=10; else c=age(n-1)+2; return(c); } main() { printf(“%d”,age(5)); } 运行结果: 18
四、知识扩展 age(5)=18 age(4)=16 age(5)=age(4)+2 =age(3)+2 age(3)=14 =age(2)+2 age(2)=12 =age(1)+2 =10
汉诺塔是一种古老的数学问题,是指在“A”柱上从下向上依次放着从大到小的一叠盘子,要把该柱上的盘子全部搬到指定的 “C”柱,在搬运过程中,可以利用“B”柱来协助搬移,但每次只允许移动一个盘子,且在移动过程中始终保持大盘在下,小盘在上。试问怎样搬移盘子?最少需要搬几次才能把“A”柱上的全部盘子移至“C”柱? A B C 汉诺塔
void move(char x,char y) { printf(“%c- - >%c\n”,x,y); } main() {int m; printf(“input the number of diskes:”); scanf(“%d”,&m); printf(“the step to moving %3d diskes:\n”,m); hanoi(m,’A’,’B’,’C’); }
A B C 需要搬的盘子的个数 借助位置 起始位置 目的地 将n个盘从A座借助B座,移到C座 void hanoi(int n,char A,char B,char C) {if(n==1)move(A,C);
A B C 需要搬的盘子的个数 借助位置 起始位置 目的地 将n个盘从A座借助B座,移到C座 void hanoi(int n,char A,char B,char C) {if(n==1)move(A,C); else{ hanoi(n-1,A,C,B);
A B C 需要搬的盘子的个数 借助位置 起始位置 目的地 将n个盘从A座借助B座,移到C座 void hanoi(int n,char A,char B,char C) {if(n==1)move(A,C); else{ hanoi(n-1,A,C,B); move(A,C);
A B C 需要搬的盘子的个数 借助位置 起始位置 目的地 将n个盘从A座借助B座,移到C座 void hanoi(int n,char A,char B,char C) {if(n==1)move(A,C); else{ hanoi(n-1,A,C,B); move(A,C); hanoi(n-1,B,A,C); } }
#include <stdio.h> int i=0; void move(char x,char y) { printf("%c- - >%c\n",x,y); i++; } void hanoi(int n,char A,char B,char C) { if(n==1) move(A,C); else { hanoi(n-1,A,C,B); move(A,C); hanoi(n-1,B,A,C); } } main() { int m; printf("input the number of diskes:"); scanf("%d",&m); printf("the step to moving %3d diskes:\n",m); hanoi(m,'A','B','C'); printf("至少需要搬%d次\n",i); }
从例题中可以看到,有些问题,采用递归的方法解决,会变得非常简单,且源程序也很简洁。从例题中可以看到,有些问题,采用递归的方法解决,会变得非常简单,且源程序也很简洁。 一个问题要采用递归的方法解决时,要符合以下条件: 1、可以把一个问题转化为一个新问题,而新问题的解决方案仍与原问题相同,只是问题的规模不同。它们只是有规律的递增或递减。 2、可以通过转化过程使问题得到解决。 3、必须有一个明确的结束递归的条件。
1.函数的嵌套调用: 在调用一个函数的过程中,又调用另一个函数。 2.函数的递归调用: 在调用一个函数的过程中,又调用它本身。
1.分析以下程序运行结果_____ ____。 #include<stdio.h> int func(int n) { if(n= =1) return 1; else return(n*func(n-1)) ; } void main() { int x; x=func(5); printf(“%d\n”,x); } 答 案 D 100 5 1 120 对不起!答错了! 对不起!答错了! 对不起!答错了! 恭喜您!答对了!
任务3 输出排序后小组三门课成绩单 一、问题情景 一个班有40位学生参加了期终考试(考了三门课),请输出学生排序后的成绩单。 分析:本项目要完成的功能相对比较多,为了使程序的结构清晰,我们可以将些项目进行分解: A:完成三门课成绩的输入;B:计算每个同学的总分与平均分;C:对三门课的成绩进行排序;D:输出函数 E:总负责,调用A、B、C、D即可。 新的知识点是:数组作为函数参数时,是如何传递的。
(三)相关知识 使用数组名作为函数参数时,实参与形参都应使用数组名(或指针变量,见项目六)。当数组名作为函数实参时,不是把数组的值传递给形参,而是把实参数组的起始地址传递给形参数组,实参和形参的地址是相同的,即当形参的值发生变化时,实参的值也发生了变化。 【例5-7】有二个学生A,B合力完成下面一个问题:求20个学生的平均成绩。他们的分工是这样的: B完成20个数的平均值,不负责数据的输入; A完成20个数的输入,然后问B要20个数的平均值后输出。
(三)相关知识 /*A所完成的程序*/ main() { int i,a[20]; float avg; printf("请输入20个同学的成绩\n"); for (i=0;i<20;i++) scanf("%d",&a[i]); avg=average(a); printf("这些同学的平均分为%.1f\n",avg); } #include "stdio.h" /*B所完成的程序*/ float average(int b[20]) { int i,s; float avg; s=0; for (i=0;i<20;i++) s=s+b[i]; avg=s/20.0; return avg; }
注 意 (1)数组名作为函数参数,应该在主调函数和被调函数中分别定义数组, 如上面程序中的b是形参数组,a是实参数组,分别在其所在的函数中定义。 #include "stdio.h" float average(intb[ ],intn) {int i,s; float avg; s=0; for (i=0;i<n;i++) s=s+b[i]; avg=(float)s/n; return avg; } (2)实参数组与形参数组类型应当相同,如果不同,将会出错,如上面程序中的形参数组b是整型,实参数组a也是整型。 (3) 实参数组与形参数组大小可以不同也可以相同,C编译器对形参数组大小不做检查,只是将实参数组的首地址传递给形参数组。 如上面程序中的float average(int b[20])改为float average(int b[10]),并不影响程序的正常运行,最后的结果也是相同的,我们甚至可以写成float average(int b[ ]),即只要b是数组即可。 (4)形参数组也可不指定大小,或者在被调函数中另设一个参数,来传递数组的大小。如上面的程序可改为: (5)形参数组与实参数组是占用同一个地址,所以是地址传递,即当形参的值发生变化时,实参的值也会跟着变化。