《(中职)C语言程序设计模块七课件.pptx》由会员分享,可在线阅读,更多相关《(中职)C语言程序设计模块七课件.pptx(61页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、(中中职职)C语语言程序言程序设计设计模模块块七七课课件件LOGO函数模块77.1函数的定义与调用7.1.1函数定义与调用程序示例讲解#include#define PI 3.14void hello()/*该函数仅实现输出一句话的功能*/printf(-Welcome to our school!-);float area(float r);/*函数定义在调用之后,须做函数原型声明*/int main()7.1函数的定义与调用7.1.1函数定义与调用程序示例讲解float a;hello();/*调用hello函数*/printf(nPlease input a:);scanf(%f,&a)
2、;printf(nThe area is:%.2f,area(a);/*调用area函数,a为实参*/float area(float r)/*自定义函数area,其功能是以r为半径求圆的面积*/float f;f=PI*r*r;return f;7.1函数的定义与调用7.1.1函数定义与调用程序示例讲解 上面程的序目的在于详细演示函数的定义与调用。该程序定义2个自定义函数,主函数基本上就起了个调用函数的作用。事实上,在一个较大的C程序中,把所用的代码都写在main()函数中是不现实的,因为在后期程序的维护和修改中极不方便。解决这个问题的方法就是自定义函数和头文件(后述)。从上可以得知以下几个
3、基本方面:(1)关于函数。函数原型也称函数声明,由函数返回类型、函数名和形参列表组成,并以分号结束。自定义函数出现在调用之前,不必声明原型;若出现在调用之后,则必须在调用之前先声明原型。一个函数若声明在某个函数体内,则只能被该函数所调用,其他函数无权使用。函数调用的方式是:函数名(实参),如sort(b,10);若定义函数时没有参数,则调用时也不能有实参,形式为:函数名(),如hello()。7.1函数的定义与调用7.1.1函数定义与调用程序示例讲解有参数函数的形参和实参类型必须一致,否则形参无法调取实参。实参可以是变量、常量、地址、数组元素的值等。实参向形参传递值有两种方式:值传递和地址传递
4、。数组名作为数组首元素的首地址,作为实参传递时本质是地址传递,从而使形参和实参都指向同一个存储单元(地址)。要实现值的批量传递(如多个数组元素的值),最好的办法就是数组名形式的地址传递。自定义函数主要为重复运算(反复调用)的程序而写。函数都是平行的,互相独立的,可调用,功能是单一的,因而在一个函数中可以声明一个函数,但不能再定义一个函数。7.1函数的定义与调用7.1.1函数定义与调用程序示例讲解有参数函数的形参和实参类型必须一致,否则形参无法调取实参。实参可以是变量、常量、地址、数组元素的值等。实参向形参传递值有两种方式:值传递和地址传递。数组名作为数组首元素的首地址,作为实参传递时本质是地址
5、传递,从而使形参和实参都指向同一个存储单元(地址)。要实现值的批量传递(如多个数组元素的值),最好的办法就是数组名形式的地址传递。自定义函数主要为重复运算(反复调用)的程序而写。函数都是平行的,互相独立的,可调用,功能是单一的,因而在一个函数中可以声明一个函数,但不能再定义一个函数。一个函数可以调用其他函数(嵌套调用),也可以调用其本身(递归调用,后述),但是绝不能调用main()这个主函数。7.1函数的定义与调用7.1.1函数定义与调用程序示例讲解函数的值只能通过return语句返回主调函数,若无return语句,则无法返回值。可以有多个return,但每次调用时只能有一个return语句被
6、执行,即只能返回一个函数值。函数值的返回类型必须与函数自定义的类型一致。如果不用给主调函数返回值,可把函数自定义为void,即空类型。定为void类型后,主调函数不可再使用被调函数的值。例如:void s(int n)/*函数s定义为void型*/;/*定义为void型,就不能使用return语句*/int main();sum=s(n);/*这是错误的,不能使用被调函数s的值*/7.1函数的定义与调用7.1.1函数定义与调用程序示例讲解(2)关于函数名和函数类型。函数名是函数的入口地址,同数组名一样,是常量。如果要交换函数名,因为是常量,是不能直接交换的,可以通过函数指针进行函数名的交换(后
7、述)。如果不定义函数类型,则其返回值默认为int型。(3)关于C程序设计。C语言是以源文件而不是以函数为单位进行编译的,函数只是构成源文件(程序)的基本单位;C语言源文件扩展名为“.C”。一个C程序由一个或多个源文件(程序)组成。自定义函数使程序简洁、易读,可以反复调用,减少内存占用,且方便调试;最为主要的是:自定义函数为实现程序的模块化设计提供了准备这是所有高级语言设计的基本方法。7.1函数的定义与调用7.1.1函数定义与调用程序示例讲解(4)关于模块的概念。模块是独立于程序的子程序或函数模块,也可以称为独立模块。每个模块只有单一功能。独立模块由顺序、选择和循环三种基本结构组成。块又称为过程
8、,执行一个过程就是调用一个子程序或函数模块。7.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递在函数调用过程中存在两种参数传递方式,即值传递和地址传递。读者要明白以下两点:(1)在这个传递过程中,永远是形参调用实参。形参把实参调过来进行运算,再把运算结果返回或在函数体中输出。(2)形参在调用实参过程中占用存储单元,该存储单元不是实参所在存储单元。那么,值传递和地址传递有什么区别呢?7.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递1.值传递在值传递方式下,形参值经过运算可以改变,但形参值的改变不会影响实参的值。例如:#include void f1(int x)x=
9、x+1;/*调得实参m的值5,经过运算,形参x值变为6*/printf(x=%dn,x);/*输出x=6*/int f2(int y)y=y+2;/*调得实参n的值5,经过运算,形参y值变为7*/return y;/*返回到主函数的值为7*/7.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递int main()int m=5,n=5,t;f1(m);/*调用函数f1,直接输出x=6*/t=f2(n);/*用t接收函数f2的返回值,即7*/printf(t=%dn,t);/*输出t=7*/printf(m=%dn,m);/*实参m值没有被改变,m仍为5*/printf(n=%dn,
10、n);/*实参n值没有被改变,n仍为5*/输出结果:x=6t=7m=5n=52.地址地址传递传递7.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递地址传递的实质是形参和实参指向的都是同一个地址,具体说来,就是相同的内存单元。因而,如果有值的变化,不分形参和实参,本质上就是同一段内存值的变化。进一步说,运算操作其实就是在实参的内存中进行的。地址传递的两种情况如下:7.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递(1)数组名作为实参传递。数组名代表数组首元素首地址,作为实参传递,传递的当然是地址,因此,要求形参必须是同类型数组或指针。例如:#include void
11、f(int a,int n)/*实参传过来数组名x,运算操作就在数组x的内存中进行*/int i;for(i=0;in;i+)ai+=i;/*给每个元素加上i值,即依次加0、1、2,结果元素值分别为10、11、12*/7.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递int main()int x3=10,10,10,i;f(x,3);for(i=0;i3;i+)printf(%dt,xi);/*调用过后数组x的元素值发生变化,依次为10、11、12*/输出结果:1011127.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递【例7-1】具有n个元素的数组a和数组b,
12、用数组a减去数组b的逆序,保存到数组c中并输出。#include int fun(int a,int b,int c,int n)/*得到数组名,其实就是在实参数组的内存中进行操作*/int i,j;for(i=0,j=n-1;i=0;i+,j-)ci=ai-bj;/*每个元素依次减去5、4、3、2、1,数组c每个元素值即为5、6、7、8、9*/7.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递【例7-1】具有n个元素的数组a和数组b,用数组a减去数组b的逆序,保存到数组c中并输出。int main()static int a5=10,10,10,10,10,b5=1,2,3,4
13、,5,c5;int i;fun(a,b,c,5);for(i=0;i5;i+)printf(%dt,ci);/*输出5、6、7、8、9*/输出结果:567897.1函数的定义与调用7.1.2函数调用过程中的值传递和地址传递(2)单个变量地址传递。在下面的例子中出现了指针运算,关于单变量地址传递在模块8中有详细叙述。读者可以在学了指针以后再返过来看本例。#include void fun(int*p1,int*p2)*p1=*p1+*p2;/*指针p1,p2分别得到变量a,b的地址,其实就是在变量a,b的内存中运算。经过对指针取内容求和运算,a的值变为5*/*p2=*p1*p2;/*b=5*3,
14、结果为15*/int main()int main()int a=2,b=3;fun(&a,&b);printf(a=%d,b=%d,a,b);/*输出a=5,b=15*/输出:a=5,b=157.1函数的定义与调用7.1.3函数定义与调用示例【例7-2】一个小小的密码验证程序。密码长度5,只能输错5次。如果密码正确,就输出“Key is right!Go to next.”;否则,就提示“Key is error.Please input key again”。#include#include int key(char a,char c);/*声明名为key的字符函数*/int main()
15、char a6,c6;/*定义两个字符数组,一个用于存放原密码,一个用于输入新密码进行验证*/7.1函数的定义与调用7.1.3函数定义与调用示例int i,j;printf(Input key:);scanf(%s,a);/*原密码输入*/putchar(n);for(i=1;i=5;i+)/*只能验证5次*/printf(Input key again:);scanf(%s,c);/*输入新的密码进行验证*/j=key(a,c);/*调用函数key。实参为原密码和新输入的密码,接收的返回值存于j*/if(j!=0)continue;/*如果新密码和原密码不相等,继续验证*/break;7.1
16、函数的定义与调用7.1.3函数定义与调用示例int key(char a1,char c1)/*定义密码验证函数*/int l;l=strcmp(a1,c1);/*调用实参,对两个密码进行比较*/if(l=0)/*如果两个密码ASCII码相等*/printf(Key is right!Go to next.);else/*否则*/printf(Key is error.Please input key again:nn);return l;/*返回比较值*/7.1函数的定义与调用7.1.3函数定义与调用示例【例7-3】素数判断。输入10个数,判断所输入的数是否为素数,如果是就输出“YES”,否
17、则就输出“No!”。#include int fun(int m)int k=1;/*注意是从1开始,1非素数*/while(k=m/2+1)&m=1return 1;elsereturn 0;7.1函数的定义与调用7.1.3函数定义与调用示例int main()int n,i;for(i=1;i=10;i+)printf(nplease enter n:);scanf(%d,&n);if(fun(n))printf(YES);elseprintf(No!);7.1函数的定义与调用7.1.3函数定义与调用示例【例7-4】直接插入排序。#include void insertsort(int R
18、,int n)int i,j,tmp;for(i=1;in;i+)if(Ri=0&tmpRj);Rj+1=tmp;int main()static int R10=1,3,2,5,7,10,6,12,30,16;int i;insertsort(R,10);for(i=0;i10;i+)printf(%4d,Ri);7.1函数的定义与调用7.1.3函数定义与调用示例【例7-5】数组元素的位移。将7个数向右依次移动2位。#include void fun1(int a,int n)int t,i;t=an-1;/*先把a6(值7)放在t中*/for(i=n-1;i0;i-)ai=ai-1;voi
19、d fun(int a,int n,int m)int i;for(i=0;im;i+)/*各元素向右滚动2次,通过调用fun1函数来实现*/fun1(a,n);int main()static int a7=1,2,3,4,5,6,7;int i;fun(a,7,2);for(i=0;i7;i+)printf(%d,ai);7.1函数的定义与调用7.1.3函数定义与调用示例【例7-6】求同构数。求110 000的同构数(如6的平方等于36,而6又出现在36的右侧,所以6是1个同构数)。#include int fun(long x,int n)int m=n,t=1,s;/*为使n保持不变,
20、用m 另行存放,t用于改变10的倍数*/while(n)s=x%(10*t);/*例如,376的平方是141 376。当376/10循环3次时,为0,将中止循环,而141 376%1 000=376*/t=t*10;n=n/10;if(s=m)return 1;/*若相等,则是同构数*/elsereturn 0;int main()int x;for(x=1;x=10000;x+)if(fun(long)x*x,x)printf(%d,x);/*输出同构数*/输出结果:156267637662593767.1函数的定义与调用7.1.3函数定义与调用示例【例7-7】求两数的最大公约数和最小公倍数
21、。#include int gys(int a,int b)int t,r;if(ba)t=a;a=b;b=t;while(r=a%b)!=0)a=b;b=r;return b;int gbs(int a,int b,int h)return a*b/h;int main()int a,b,h,l;printf(Input two integer numbers:n);scanf(%d%d,&a,&b);h=gys(a,b);l=gbs(a,b,h);printf(The MAX gys is:%dn,h);printf(The MIN gbs is:%dn,l);输入/输出示例:Input
22、two integer numbers:2736The MAX gys is:9The MIN gbs is:1087.1函数的定义与调用7.1.3函数定义与调用示例【例7-8】哥德巴赫猜想。验证100以内大于2的正偶数都能分解成两个素数之和。思路:把4100以内的正整数进行分解,分解成两个数,分别调用函数判断两个数是否为素数;如果是,就输出求和的表达式。#include int ifss(int i)/*函数功能是判断一个数是否为素数*/int j;if(i=1)return 0;/*小于1的数不是素数*/if(i=2)return 1;/*2是素数*/for(j=2;ji;j+)/*对大于
23、2的数进行判断*/if(i%j=0)return 0;else if(i!=j+1)continue;elsereturn 1;7.1函数的定义与调用7.1.3函数定义与调用示例int main()int i,j,k,flag1,flag2,n=0;for(i=4;i100;i+=2)/*对4100的正偶数进行拆分*/for(k=2;k=i/2;k+)j=i-k;/*把正偶数i拆分成j和k*/flag1=ifss(k);/*调用函数,判断k是否为素数*/if(flag1)flag2=ifss(j);/*调用函数,判断j是否为素数*/if(flag2)printf(%d=%d+%d,i,k,j)
24、;n+;if(n%7=0)/*每行输出7个*/printf(n);printf(n);7.1函数的定义与调用7.1.3函数定义与调用示例【例7-9】尼科彻斯定理。任何一个整数的立方都可以写成一串连续奇数的和。思路:先确定这串连续奇数的最大值的范围。确定方法是:任何立方值(如下,设为sum)的一半(如下,设为i)如果是奇数,则i+i+2的值一定大于sum,那么这串连续奇数的最大值不会超过i;如果i是偶数,这串连续奇数的最大值不会超过i+1,如5的立方为125,i=125/2=62,则其奇数的最大值不会超过63。确定了范围后就可以从最大值开始进行穷举。7.1函数的定义与调用7.1.3函数定义与调用
25、示例#include void nksdl(int n)int i,j,k=0,l,m,sum,flag=1;m=n*n*n;i=m/2;/*立方值的一半*/if(i%2=0)i=i+1;/*如果i是偶数,则奇数的最大值为 i+1*/while(flag=1&i=1)/*奇数必须大于或等于1*/sum=0;k=0;while(1)sum+=(i-2*k);/*奇数和。奇数减有序偶数(从0开始的按序偶数)得有序奇数*/k+;/*有序递增,同时记录了奇数个数*/if(sum=m)printf(%d*%d*%d=%d=,n,n,n,m);for(l=0;lm)break;i-=2;/*如果上面的最大
26、奇数得不出正确结果,则最大奇数值下移一个*/int main()int n;printf(Input a integer:n);scanf(%d,&n);nksdl(n);输出示例:Input a integer:55*5*5=125=129+27+25+23+217.2变量的作用域和生存期7.2.1局部变量和全局变量只在某个函数体和或复合语句中起作用的变量称为局部变量。或者说,定义在某个函数体内部的变量,只能为该函数所用;定义在某个复合语句里面的变量,只能为该复合语句使用。根据变量的作用范围,变量可以分为局部变量和全局变量。1.局部变量7.2变量的作用域和生存期7.2.1局部变量和全局变量下
27、例充分体现了局部变量的作用域。#include void a()/*该函数功能是比较输入的3个数的大小*/int x,y,z;/*局部变量x,y,z,只在该函数体里面有效*/printf(Please input x,y,z:);scanf(%d%d%d,&x,&y,&z);printf(The Max is:%d,(xy?x:y)z?(xy?x:y):z);void b()/*该函数判断输入的3个整型值是否能组成一个直角三角形*/int x,y,z;/*局部变量x,y,z,只在该函数体里面有效*/printf(Please input x,y,z:);scanf(%d%d%d,&x,&y,&
28、z);if(x*x+y*y=z*z|x*x+z*z=y*y|y*y+z*z=x*x)printf(It is a Delta);elseprintf(It isnt a Delta);7.2变量的作用域和生存期7.2.1局部变量和全局变量putchar(n);int x,y,z;/*复合语句体在程序中由 组成。此处的复合语句体变量x,y,z只在该复合语句体内起作用*/printf(Please input x,y,z:);scanf(%d%d%d,&x,&y,&z);printf(%.2f,(float)(x+y+z)/3);int main()a();putchar(n);b();3次输入1
29、,2,3,输出结果:Please input x,y,z:1 2 3The Max is:3Please input x,y,z:1 2 3It isnt a DeltaPlease input x,y,z:1 2 32.007.2变量的作用域和生存期7.2.1局部变量和全局变量在整个程序运行中均起作用的变量称为全局变量,它的作用域是整个程序。全局变量定义于函数体外,它不属于哪一个函数,因而也称外部变量。全局变量从定义的地方开始向下起作用,向上无效。如果某个函数体要使用下面的全局变量,则应在该函数体中用extern关键字对该全局变量进行声明。2.全局变量7.2变量的作用域和生存期7.2.1局部
30、变量和全局变量下例充分体现了全局变量的含义及其和局部变量的区别。#include int x=10,y=11,z=12;/*全局变量x,y,z*/void a()int x=1,y=2,z=3;/*局部变量x,y,z*/printf(x+y+x=%dn,x+y+z);/*在函数体内,局部变量优于全局变量,输出局部变量之和6*/int x1=4,y1=5,z1=6;/*全局变量x1,y1,z1*/void b()printf(x1+y1+z1=%dn,x1+y1+z1);/*没有同名的局部变量,全局变量起作用,输出15*/printf(x+y+x=%dn,x+y+z);/*没有同名的局部变量,全
31、局变量起作用,输出33*/int main()7.2变量的作用域和生存期7.2.1局部变量和全局变量extern l,m,n;/*要使用main()函数下面的全局变量l,m,n,须在函数体内做extern声明*/a();b();printf(x1+y1+z1=%dn,x1+y1+z1);/*没有同名的局部变量,全局变量起作用,输出15*/printf(l+m+n=%d,l+m+n);/*在函数体内做了extern声明,可以使用,输出27*/int l=8,m=9,n=10;输出结果:x+y+x=6x1+y1+z1=15x+y+x=33x1+y1+z1=151+m+n=277.2变量的作用域和生
32、存期7.2.2动态变量和静态变量C语言的变量和函数都有两个属性,一是数据类型,二是存储类型。按变量生存期划分,变量的存储类型可以分为动态存储(auto)和静态存储(static),因而可以把变量分为动态变量和静态变量。1.动态变量和静态变量的概念(1)动态变量。在程序运行中动态分配存储空间,函数运行结束便立即释放,即为动态变量,如“int a,b,c;”,a、b、c这3个变量都是动态变量。动态变量赋值在函数调用时进行,如果不赋值,则值不确定。动态变量也称自动变量,用auto关键字进行声明。例如:auto int a,b,c;在书写时关键字auto常常省略,故“auto int a,b,c;”等
33、同于“int a,b,c;”。7.2变量的作用域和生存期7.2.2动态变量和静态变量(2)静态变量。静态变量在内存中获得固定的存储空间,程序运行期间不被释放。静态变量用static关键字进行声明。例如:static int a,b,c;静态变量的值只赋值1次,不赋值则默认为0(数值型)或0(字符型)。在函数调用时若有新赋值,则在原来值的基础上变化。7.2变量的作用域和生存期7.2.2动态变量和静态变量2.动态变量和静态变量运行过程中的变化下面的程序充分地体现了动态变量和静态变量在程序运行过程中值的变化情况。动态变量的值调用一次被释放一次,打回原形;而静态变量的值不被释放。int main()i
34、nt i,a=2;for(i=0;i3;i+)printf(%-3d,f(a);putchar(n);for(i=0;i3;i+)printf(%-3d,f(i);#include int f(int a)int b=3;static c=6;b=b+2;c=c+5;return(a+b+c);7.2变量的作用域和生存期7.2.2动态变量和静态变量输出结果:18 23 2831 37 43其中,a、b是动态变量,c是静态变量。第1个for循环,3次调用f(a)的计算过程是:(1)第1次循环:a=2,b=3,c=6,b=b+2=5,c=c+5=11,a+b+c=18。(2)第2次循环:a=2,b
35、=3(释放回原形),c=11(不被释放),b=b+2=5,c=c+5=16,a+b+c=23。(3)第3次循环:a=2,b=3(释放回原形),c=16(不被释放),b=b+2=5,c=c+5=21,a+b+c=28。第2个for循环只是函数调用参数改为了i,运算过程同上。7.2变量的作用域和生存期7.2.2动态变量和静态变量(1)为了提高程序的运行效率,C语言允许将部分局部变量的值存放于CPU的寄存器中,这种变量就称为寄存器变量。(2)寄存器变量用register关键字进行声明。例如:register int a,b,c;/*声明了a,b,c三个寄存器变量*/(3)声明寄存器变量的目的是提高程
36、序的运行效率,因而要注意以下几点:不能把静态变量声明为寄存器变量,只有局部动态变量和形参可以作为寄存器变量。寄存器数目有限,不能定义任意多个寄存器变量。3.寄存器变量7.3变量的存储栈和堆变量是存放于内存中的。如前所述,从整体上来说,变量的存储类型可以分为动态存储和静态存储。具体而言,对一个程序员来说,内存可以分为五个部分:静态区、堆区、栈区、文字常量区和程序代码区。下面简要了解一下这五个区的存储区别。(1)文字常量区存放程序中的字符串常量,程序结束后由系统释放。(2)程序代码区存放函数体的二进制代码。(3)静态区。存放全局变量。执行程序时给全局变量分配固定存储区,程序执行完毕就释放,即全局变
37、量在程序执行过程中占据固定的存储单元,而不动态地进行分配和释放。存放静态变量。静态变量在整个程序运行期间都不被释放,因而静态变量的生命周期要长于全局变量。(4)栈区(stack)。(5)堆区(heap)。栈区存放函数的形参、自动变量、函数调用时的现场保护和返回地址以及寄存器变量等,内存空间由编译器自动分配和释放。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被清除,因此其特点是效率高但空间大小有限。变量在堆区的内存空间由程序员用 malloc()函数动态分配,使用完毕用free()函数进行释放。在释放之前它是一直存在的,直到程序结束。因此其特点是使用灵活,空间比较大,但容易出
38、错。7.3变量的存储7.4递 归 函 数所谓递归函数,指的是某一函数反复调用其自身,每调用一次就进入新的一层,直到符合条件退出(终止)调用。为避免函数无休止地调用自身,须把握递归函数的下面两个关键点:(1)把握各项之间的规律性。(2)正确设置递归出口,即终止递归调用的条件。7.4递 归 函 数7.4.1递归调用示解求阶乘【例7-10】从键盘输入一个数,求其阶乘。#include long ff(int n)long f;if(n0)printf(Error);/*如果调过来的实参是小于0的,则输出“Error”,退出函数*/else if(n=0|n=1)/*这是退出递归调用的条件。n=1或0
39、时,终止递归调用*/f=1;elsef=ff(n-1)*n;/*否则,执行自身调用*/int main()int m;long y;scanf(%d,&m);y=ff(m);printf(%d!=%ld,m,y);7.4递 归 函 数7.4.1递归调用示解求阶乘假设在主函数中输入5,执行函数调用ff(m)。(1)在递归函数ff中,第1次得到实参5,执行else,有f=ff(n-1)*5,即f=ff(4)*5。这时递归调用的参数变为了n-1,即4。(2)第2次,把实参4传递给形参n,执行else,有f=ff(n-1)*4*5,即f=ff(3)*4*5。这时递归调用的实参变为了3。(3)第3次,把
40、实参3传递给形参n,执行else,有f=ff(n-1)*3*4*5,即f=ff(2)*3*4*5。这时递归调用的实参变为了2。(4)第4次,把实参2传递给形参n,执行else,有f=ff(n-1)*2*3*4*5,即f=ff(1)*2*3*4*5。这时递归调用的实参变为了1。(5)第5次,把实参1传递给形参n,执行else if,即ff(1)的结果为1,则“f=1*2*3*4*5=120”。7.4递 归 函 数7.4.1递归调用示解求阶乘下面用上例来进一步把握递归调用的两个关键点:(1)把握各项之间的规律性。本例中的阶乘就是从1开始的自然数相乘,各项之间步长为1,即有(n-1)*n这样的规律性
41、。因此递归调用时实参减1,如本例中的ff(n-1)。(2)设置第1项为终止递归调用的条件。本例中,当n的值到了1或0时,就终止了递归。1是求阶乘的起始元素,0是调用时可能存在的情况,即ff(n-1),实参n=n-1=0。7.4递 归 函 数7.4.1递归调用示解求阶乘对比本例非递归调用的代码之一如下:#include long f(int n)long cj=1;int i;for(i=1;i=n;i+)cj*=i;return cj;int main()int n;printf(Input n:);scanf(%d,&n);printf(%d!=%ld,n,f(n);对比之下,递归调用似乎不
42、存在优势,但在某些情况(如某一类操作方式的大量重复)下,递归调用却是最好的解决方法。7.4递 归 函 数7.4.2递归调用的应用【例7-11】求第n个奇数的值。按递归函数的关键点分析:奇数的相邻项之间步长为2;项名即第1,2,3之间步长为1。故项名作为实参调用,递归到第1项时便终止递归。递归调用中的实参应为n-1。假如递归函数名为qs,则可得到递归调用的表达式为:qs(n-1)+2。int n;printf(Input n:);scanf(%d,&n);printf(NO.%d qs is:%d,n,qs(n);如输入1 000,输出结果为:Input n:1000NO.1000 qs is:
43、1999#include int qs(int n)if(n=1|n=0)return 1;/*改为0则可求偶数。因为0为第1个偶数*/elsereturn qs(n-1)+2;int main()7.4递 归 函 数7.4.2递归调用的应用【例7-12】求表达式前n项之和。用递归函数求13+35+57+前n项之和。分析:(1)对于递归函数,第1项一般设置为退出条件,故可得一个语句:if(n=1)return 3;(2)递归函数中项按递减运算,直到项递归到第1项时终止递归调用。故递归调用中(假设递归函数名为f),项递减代码为:f(n-1)(3)找出项的规律性。该题中项的数学规律是:(2n-1)
44、(2n+1)(4)确定项与项之间的运算符,为求和。故可得到:f(n-1)+(2*n-1)*(2*n+1)7.4递 归 函 数7.4.2递归调用的应用【例7-13】汉诺塔。一块板上有3根针A、B、C。A针上套有n个大小不等的圆盘,大的在下,小的在上。要把这n个圆盘从A针移到C针上,每次只能移动一个圆盘,移动可以借助B针进行,但在任何时候、任何针上的圆盘都必须保持大盘在下、小盘在上。求移动的步骤。7.4递 归 函 数7.4.2递归调用的应用#include movedisk(int n,char A,char B,char C)if(n=1)printf(%c-%cn,A,C);else move
45、disk(n-1,A,C,B);printf(%c-%ct,A,C);movedisk(n-1,B,A,C);int main()int n;printf(Input n:);scanf(%d,&n);printf(The step to move%d diskes:n,n);movedisk(n,A,B,C);故可得以下程序:7.4递 归 函 数7.4.2递归调用的应用【例7-14】快速排序法。快速排序法是冒泡排序的一种改进。其思路为:在待排序的n个数据中取第1个数据作为基准值,将所有记录分为3组,使第1组中各数据值均小于或等于基准值,第2组实际上就只有一个基准值,第3组中各数据值均大于或等
46、于基准值。这样便实现了第一趟分割,然后对第1组和第3组分别重复上述方法,以此类推,直到每组中只有一个记录为止。#include void quicksort(int a,int start,int end)int i,j,key;i=start;/*将每组首个元素下标赋给i*/j=end;/*将每组末尾元素下标赋给j*/key=astart;/*设置每组第1个元素值为基准值*/while(ij)while(ij&key=aj)7.4递 归 函 数7.4.2递归调用的应用j-;/*定位j到比基准值小的元素位置*/if(ij)ai=aj;/*将上述较小的元素值放到(每组)第1个元素位置*/i+;w
47、hile(ij&ai=key)i+;/*定位i到比基准值大的位置。若i、j重合,退出里外所有循环,执行下面的ai=key。这时,以基准值为界,数组段分成2组,前一组小于或等于基准值。后一组大于或等于基准值*/if(ij)aj=ai;/*将大于基准值的ai放到aj位置*/j-;/*位置左移*/7.4递 归 函 数7.4.2递归调用的应用ai=key;/*将基准值放入指定位置*/if(starti)quicksort(a,start,j-1);/*j-1表示对分割成的第一组重复执行上述步骤*/if(iend)quicksort(a,j+1,end);/*j+1表示对执行quicksort(a,st
48、art,j-1)后产生的第2组重复执行上述步骤*/int main()int b7=0,3,5,10,9,7,12,i;quicksort(b,0,6);for(i=0;i=1)return an-1+f(a,n-1);elsereturn 5;int main()int b5=10,20,30,40,5,s;s=f(b,5);printf(%c,s);7.4递 归 函 数7.4.2递归调用的应用运算过程分析,调用数组b:A.当n=1时,执行“return an-1+f(a,n-1);”语句。B.否则,执行“return 5;”退出函数调用。具体运算过程如下:第1次,实参n=51,执行a4+f
49、(a,4),即5+f(a,4)。第2次,实参n=41,递归调用f()函数,f(a,4)调用的结果是:5+a3+f(a,3),即5+40+f(a,3),得45+f(a,3)。第3次,实参n=31,递归调用f()函数,f(a,3)调用的结果是:45+a2+f(a,2),即45+30+f(a,2),得75+f(a,2)。第4次,实参n=21,递归调用f()函数,f(a,2)调用的结果是:75+a1+f(a,1),即75+20+f(a,1),得95+f(a,1)。第5次,实参n=1,递归调用f()函数,f(a,1)调用的结果是:95+a0+f(a,0),即95+10+f(a,0),得105+f(a,0)。第6次,实参n=01,执行else return 5,退出函数调用。故此时内存中的值为105+5110。LOGO