版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C语言程序设计第5章模块化程序设计第5章内容模块化程序设计思想函数的定义函数的调用函数的声明函数的嵌套调用函数的递归调用变量的作用域变量的存储类型内部函数和外部函数模块化程序设计思想在软件工程中,通常采用“自顶向下、分而治之”的方法,将大问题分解成若干小的问题,如果小问题还不容易解决,就再分解成更小的问题。模块化程序设计的基本思想,是将一个大的复杂的程序按功能分割成一些小的功能模块。当开发一个软件系统时,最好的办法是从编写主程序开始,在主程序中,将问题作为一个整体考虑,找出完成任务的主要步骤,再沿着这条主线将整个问题继续分解为独立的简单模块,这就是模块化程序设计的主要思想。模块分解的原则模块分解——“自顶向下、逐步求精”的程序设计过程模块分解的基本原则——高聚合、低耦合及信息隐藏高聚合——一个模块只能完成单一的功能,不能“身兼数职”低耦合——模块之间参数传递尽量少,也不能通过全局变量来实现数据传递信息隐藏——把不需要调用者知道的信息都包装在模块内部隐藏起来凡是被众多模块公用的程序,均应设计成一个独立的模块各模块间应在功能上、逻辑上相互独立,模块间的接口应尽量简单,其数据传递使用参数来完成每个模块应设计成单入口、单出口形式,以便调试与阅读,提高程序的可靠性模块化设计方法功能分解自顶向下、逐步求精的过程。每个模块的功能尽量单一,程序代码最好不要超过50行。模块分解的原则保证模块的相对独立性(高聚合、低耦合)。模块的实现细节对外不可见(外部:关心做什么;内部:关心怎么做)。设计好模块接口接口是指罗列出一个模块的所有的与外部打交道的变量等。定义好后不要轻易改动。在模块开头(文件的开头)进行函数声明。模块化程序设计的特点各模块相对独立、功能单一、结构清晰、接口简单程序设计的复杂性得到了有效控制缩短开发周期避免程序开发的重复劳动易于维护和功能扩充模块与函数在C语言中,每个模块都是由函数完成的,一个小模块就是一个函数。将一些常用的功能模块编写成函数,放在函数库中,组成C库函数,供公共选用。编写某个函数时,遇到具有相对独立功能的程序段,都应独立成另一个函数,供另一个函数调用;当某一个函数代码较长时,也应将其中相对独立的代码分成另一个函数从函数定义的角度看,C语言的函数可分为库函数、用户定义函数(包括主函数main)两种。从函数的使用性质来看,C语言的函数还分为内部函数、外部函数两种。函数的本质是外部函数。函数设计的原则函数的功能要单一,不要设计多用途的函数函数的规模要小,尽量控制在50行代码以内1986年IBM在OS/360的研究结果:大多数有错误的函数都大于500行1991年对148,000行代码的研究表明:小于143行的函数比更长的函数更容易维护参数和返回值的规则参数要书写完整,不要省略对函数的入口参数进行有效性检查没有参数和返回值时,用void填充每个函数只有一个入口和一个出口,尽量不使用全局变量尽量少用静态局部变量,以避免使函数具有“记忆”功能函数的定义合法标识符函数返回值类型:缺省int型,无返回值void函数体函数类型函数名(形式参数类型说明表){
声明部分 执行部分}现代风格:例有参函数(现代风格)
intmax(intx,inty){intz;z=x>y?x:y;return(z);}例空函数
dummy(){}函数体为空例无参函数voidprintstar(){printf(“**********\n”);}或voidprintstar(void){printf(“**********\n”);}参数:传递数据信息的通道函数定义说明①类型说明符可为:int、char、float、double等。表示在调用了该函数后,其返回值的数据类型;如果函数无数据返回时,应使用void
作类型定义符。注意,省略了类型说明符,编译程序默认的函数返回值为一个整型值类型(int)。②函数名,由用户取的合法标识符。C语言的关键字不能作函数名。自定义函数的名称可以使用库函数名,但这时库函数被屏蔽。③形式参数表是一个用逗号分隔的变量表,当函数被调用时这些变量接受调用参数的值。相当于函数调用时传递信息的通道。④在函数的定义中,如果没有函数体,即函数什么功能都不做,称为空函数。空函数的功能主要是在程序设计中,留出该函数的功能,以后在需要的时候补充上去。特别注意(1)函数不能单独运行,函数可以被主函数或其它函数调用,也可以调用其它函数,但是不能调用主函数。(2)C规定,函数体的声明部分和执行部分应严格划分,且声明部分放在函数体的开始。例如,以下定义是不允许的:voidmain(){doublex;scanf("%lf",&x);
doubles;/*不能在执行语句中穿插定义变量*/scanf("%lf",&s);…}主调函数与被调函数C语言程序是由若干个函数组成的,各函数在结构上是独立的,但它们所处理的对象即数据却是相互联系的。一个函数调用另一个函数,将调用的函数称为主调函数,将被调用的函数称为被调函数。主调函数和被调函数具有数据传递的关系,其传递的实施是通过函数的参数来实现的。主调函数和被调函数的概念是相对的。函数调用的一般形式#include<stdio.h>voidmain(){inta=5,b=7,c;c=max(a,b);printf(“较大的数是%d",c);}
intmax(intx,inty){intz;z=x>y?x:y;return(z);}作为一个表达式调用,调用后只是返回一个结果值函数调用过程中的参数传递c=max(a,b);
intmax(intx,inty){intz;z=x>y?x:y;return(z);}按顺序对应参数传递实参数形式参数函数调用过程中的结果返回c=;
intmax(intx,inty){intz;z=x>y?x:y;return(z);}max(a,b)57=77777无返回函数的调用#include<stdio.h>voidmain(){printstar();}voidprintstar(){printf(“**********\n”);}作为一个函数语句调用函数调用说明如果调用的是无参函数,“实际参数表”可以没有,但括号()不能省略。如果实参表包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等、类型应一致。实参与形参按顺序对应,一一传递数据。函数不能嵌套定义,即在一个定义好的函数中,不能又定义另一个函数。函数之间允许相互调用,也允许嵌套调用。但main函数不能被调用。函数还可以自己调用自己,称为递归调用。补充实例1求任意正整数n的阶乘n!=1×2×3×……×n计算阶乘#include<stdio.h>voidmain(){ longi,m,n; printf("请输入一个正整数:");
scanf("%d",&n); //键盘输入一个正整数n
m=1; for(i=2;i<=n;i++){ m*=i; } printf("%d!=%d\n",n,m);}编写一个计算阶乘的函数longfact(intn){ longi,m;
m=1; for(i=2;i<=n;i++){ m*=i; } return(m);}补充实例2计算以下公式的值(其中n为键盘输入的任意数)sum=1!+2!+3!+……+n!计算阶乘之和#include<stdio.h>voidmain(){ longi,sum=0,n; printf("请输入一个正整数:");
scanf("%d",&n);
for(i=1;i<=n;i++){ sum+=fact(i); } printf("计算结果=%d\n",sum);}longfact(intn){longi,m;
m=1;for(i=2;i<=n;i++){ m*=i;}return(m);}补充实例3计算以下公式的值(其中n为键盘输入的任意数)longfact(intn){longi,m;
m=1;for(i=2;i<=n;i++){ m*=i;}return(m);}计算多项式之和的程序#include<stdio.h>voidmain(){ longi,n; doublesum=1.0; printf("请输入一个正整数:");
scanf("%d",&n); for(i=1;i<=n;i++){ sum+=1.0/fact(i); } printf("计算结果=%f\n",sum);}判断一个数是否为素数的程序#include<stdio.h>#include<math.h>voidmain(){inti,m,flag;printf("请输入一个正整数m>2:");
scanf("%d",&m); //键盘输入一个正整数m
flag=1;
//假设m是素数
for(i=2;i<=sqrt(m)+1;i++){if(m%i==0) //若能被i整除,返回1,说明m不是素数{ flag=0; break;} //跳出循环,减少无效的循环次数}
if(flag==1)printf("%d是素数\n",m);elseprintf("%d不是素数\n",m);}补充实例4找出50~100之间的所有素数,并在屏幕上输出找出50~100之间的所有素数#include"stdio.h"#include"math.h"voidmain(){ inti,j,flag; for(i=50;i<=100;i++) { flag=1; for(j=2;j<=sqrt(i)+1;j++) { if(i%j==0) { flag=0; break; } } if(flag==1)printf("%d",i); }}自定义一个专门判断是否素数的函数intPrime(intnumber){inti,flag=1;
for(i=2;i<=sqrt(number)+1;i++){ if(number%i==0) { flag=0; break;} }
return(flag);
}找出50~100之间的所有素数#include<stdio.h>#include<math.h>voidmain(){ inti;
for(i=50;i<=100;i++){ if(Prime(i))
{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }
return(flag);
}500找出50~100之间的所有素数#include<stdio.h>#include<math.h>voidmain(){ inti;
for(i=50;i<=100;i++){ if(Prime(i))
{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }
return(flag);
}510找出50~100之间的所有素数#include<stdio.h>#include<math.h>voidmain(){ inti;
for(i=50;i<=100;i++){ if(Prime(i))
{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }
return(flag);
}520找出50~100之间的所有素数#include<stdio.h>#include<math.h>voidmain(){ inti;
for(i=50;i<=100;i++){ if(Prime(i))
{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }
return(flag);
}531自定义头文件student.h#include<stdio.h>#include<math.h>#include"e:\student.h"voidmain(){ inti;
for(i=50;i<=100;i++){ if(Prime(i))
{ printf("%5d",i); } }}把自定义函数保存在自行编写的头文件中,是一个方便实用的方法。用户自行编写的头文件,最好不要与系统提供的头文件保存在同一路径下。补充实例5查找1000以内的最大素数查找1000以内最大的素数#include"stdio.h"#include"math.h"voidmain(){ inti,j,flag; for(i=1000;i>=2;i--) //注意循环控制方法的改变 { flag=1; for(j=2;j<=sqrt(i)+1;j++) { if(i%j==0) { flag=0; break; } } if(flag==1) { printf("1000以内的最大素数是:%d\n",i); break; } }}查找1000以内的最大素数#include<stdio.h>#include<math.h>voidmain(){ inti,m; for(i=1000;i>=2;i--){ if(Prime(i)) { printf("1000以内的最大素数为%d\n",i); break; } }}补充实例6哥德巴赫猜想:一个足够大的偶数,必定可以表示成两个素数之和分析思路:设计一个判断任意指定数是否为素数的子函数把输入的偶数拆分成两个数,并分别调用上述子函数,判断它们是否为素数例如:128=19+109验证哥德巴赫猜想的程序代码#include<stdio.h>#include<math.h>voidmain(){inti,j,m,flag1,flag2;printf("请输入一个足够大的偶数:");scanf("%d",&m);for(i=2;i<=m/2;i++) {
flag1=1; for(j=2;j<=sqrt(i)+1;j++) { if(i%j==0) { flag1=0; break; } }
flag2=1; for(j=2;j<=sqrt(m-i)+1;j++) { if((m-i)%j==0) { flag2=0; break; } } if(flag1==1&&flag2==1) { printf("%d=%d+%d\n",m,i,m-i); break; } }}验证哥德巴赫猜想的程序代码#include<stdio.h>#include<math.h>voidmain(){ inti,m;printf("请输入一个足够大的偶数:");
scanf("%d",&m); //键盘输入一个正整数m
for(i=2;i<=m/2;i++){ if(Prime(i)&&Prime(m-i)) { printf("%d=%d+%d\n",m,i,m-i); break; } }}课堂作业1写出可以根据主调函数提供的数值n,用来计算以下多项式之和的函数sum=1+2+3+……+n课堂作业2编写一个能根据提供的数值n,在屏幕上输出一行n个星号的函数(n由主调函数传递而来)课堂作业3编写一个能根据提供的数值n,在屏幕上输出由星号组成的如下图案的函数(本例设n=4)****************课堂作业4编写一个能根据提供的数值n,在屏幕上输出由星号组成的如下图案的函数(本例设n=5,并限制n小于10)*************************课堂作业5判断键盘输入的一个正整数是不是回文数所谓回文数,就是从左看或从右看均是同一个数的数,如2552、73837判断是否回文数的程序#include<stdio.h>voidmain(){longm=0,n,p;printf("请输入一个正整数:");scanf("%d",&n); //键盘输入一个正整数n p=n; while(p>0) { m=m*10+p%10; p=p/10; } if(n==m) printf("%d是回文数\n",n); else printf("%d不是回文数\n",n);}回文数计算过程x=32768y=0y=y*10+x%10=0*10+8=8x=x/10=3276y=y*10+x%10=8*10+6=86x=x/10=327回文数计算过程y=y*10+x%10=86*10+7=867x=x/10=32y=y*10+x%10=867*10+2=8672x=x/10=3y=y*10+x%10=8672*10+3=86723x=x/10=0课堂作业5写出判断给定的数n是否为回文数的函数,若n为回文数,函数返回值为1,否则返回值为0课堂作业1答案#include<stdio.h>intsum(intx){ inti,s=0; for(i=1;i<=x;i++) s+=i; return(s);}voidmain(){intn;printf("请输入一个正整数:");scanf("%d",&n); //键盘输入一个正整数n printf("累加和=%d\n",sum(n));}课堂作业2答案#include<stdio.h>voidstars(intx){ inti; for(i=1;i<=x;i++) printf("*"); printf("\n");}voidmain(){intn; printf("请输入一个正整数:");scanf("%d",&n); //键盘输入一个正整数n stars(n); //注意调用方式}课堂作业3答案#include<stdio.h>voidstars(intx){ inti,j; for(i=1;i<=x;i++) { for(j=1;j<=2*i-1;j++) printf("*"); printf("\n"); }}voidmain(){intn; printf("请输入一个正整数:");scanf("%d",&n); //键盘输入一个正整数n stars(n);}课堂作业4答案#include<stdio.h>voidstars(intx){ inti,j,k; for(i=1;i<=x;i++) { for(k=1;k<20-2*i+1;k++) printf(""); for(j=1;j<=2*i-1;j++) printf("*"); printf("\n"); }}voidmain(){intn; printf("请输入一个正整数:");scanf("%d",&n); //键盘输入一个正整数n stars(n);}课堂作业5答案charhuiwen(longn){ longm=0,p; p=n; while(p>0) { m=m*10+p%10; p=p/10; } if(m==n) return(1); else return(0);}找出1~999之间的所有回文数#include<stdio.h>voidmain(){longi;for(i=1;i<=999;i++) { if(huiwen(i)) printf("%5d",i); }}找出n和n2均为回文数的数#include<stdio.h>voidmain(){longi;for(i=1;i<=999;i++) { if(huiwen(i)&&huiwen(i*i)) printf("%10d%10d\n",i,i*i); }}例5.1【例5.1】求三个数中最大数和最小数的差值。分析问题:首先在主函数main()中调用求差值的函数dif()在求差值函数中再调用求三个数的最大值函数max()和最小值函数min()将一个复杂的问题分解成了三个小问题:求两个数的差求三个数的最大值求三个数的最小值程序实现#include<stdio.h>/*自定义函数的声明*/intdif(intx,inty,intz);intmax(intx,inty,intz);intmin(intx,inty,intz);intmain(){inta,b,c,d;printf("InputData:");scanf("%d%d%d",&a,&b,&c);/*输入三个数*/
/*调用差值函数,并将函数的返回值赋给d*/d=dif(a,b,c);printf("Max-Min=%d\n",d);/*输出*/return(0);}程序实现(续)/*定义求三个数x、y、z的差值函数dif*/intdif(intx,inty,intz){intm1,m2;m1=max(x,y,z); /*调用求最大值函数,返回值赋给m1*/m2=min(x,y,z); /*调用求最小值函数,返回值赋给m2*/returnm1-m2; /*返回最大值与最小值的差*/}/*定义求三个数x、y、z的最大值函数max*/intmax(intx,inty,intz){intr1,r2;r1=(x>y)?x:y;r2=(r1>z)?r1:z;return(r2); /*返回三个数的最大值*/}/*定义求三个数x、y、z的最小值函数min*/intmin(intx,inty,intz){intr;r=(x<y)?x:y;return(r<z?r:z); /*返回三个数的最小值*/}程序的调用关系main()调用函数dif输出结束dif函数max函数调用函数max调用函数minmin函数本例程序涉及到模块化程序设计思想、函数的定义、函数的调用、函数的声明、变量的作用域、变量的存储类型、内部函数和外部函数等知识点。例5.2【例5.2】用函数实现:编程求的值。分析问题:可以定义一个函数doublefff(doublex)来求值,然后在主函数main()中调用该函数。例5.2程序#include<stdio.h>doublefff(doublex){doublef;f=x*x;return(f);}/*主函数中调用fff()函数*/intmain(){doublef=0.3,ff;ff=fff(f);printf(“%f\n”,ff);return(0);}函数参数传递形参与实参形式参数:定义函数时函数名后面括号中的变量名实际参数:调用函数时函数名后面括号中的表达式c=max(a,b);(main函数)(max函数)max(intx,inty){intz;z=x>y?x:y;return(z);}例5.3
编程求两个数的最大值#include<stdio.h>intmax(intx,inty){intz;z=x>y?x:y;return(z);}intmain(){inta,b,c;scanf("%d,%d",&a,&b);c=max(a,b);printf("Maxis%d",c);return(0);}形参实参(主调函数)c=max(a,b);(被调函数)max(intx,inty)实际参数形式参数参数传递参数传递——值传递方式值传递方式方式:函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值特点:形参与实参占用不同的内存单元单向传递实参a10形参x10复制实参内存空间形参内存空间参数传递——地址传递方式地址传递方式:函数调用时,将数据的存储地址作为参数传递给形参特点:形参与实参占用同样的存储单元“双向”传递实参和形参必须是地址常量或变量实参a形参x实参内存空间形参内存空间函数的返回值返回语句形式:return(表达式);或return表达式;或return;功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数说明:函数中可有多个return语句,但只能返回唯一的函数值若无return语句,遇
}
时,自动返回调用函数,返回值是一个不确定的值若函数类型与return语句中表达式值的类型不一致,按函数类型为准,表达式值类型在函数调用时自动转换成函数类型void型函数:表示“空类型”或“无类型”例无返回值函数
voidswap(intx,inty){inttemp;temp=x;x=y;y=temp;}例有返回值函数intmaxmum(intx,inty){intz;z=(x>y)
?x:y
;return(z);/*返回最大值*/}函数返回不确定值
intprintstar(){printf("**********");}intmain(){inta;a=printstar();printf("%d",a);return(0);}输出:10voidprintstar(){printf("**********");}intmain(){inta;a=printstar();printf("%d",a);return(0);}编译错误!函数返回值类型转换#include<stdio.h>intmain(){floata,b;intc;scanf("%f,%f",&a,&b);c=max(a,b);printf("Maxis%d\n",c);return(0);}intmax(floatx,floaty){floatz;z=x>y?x:y;return(z);}函数值转换成float函数返回值类型int例5.4【例5.4】用函数实现:编程计算两个数之和。分析问题:可以定义一个函数intadd(intx,inty)来求两个数之和的值,并通过return语句将和值返回到主函数中。例5.4程序#include<stdio.h>intadd(intx,inty){intz;z=a+b;return(z);}intmain(){inta,b,c;scanf("%d%d",&a,&b);c=add(a,b);printf("sum=%d\n",c);return(0);}函数声明对被调用函数要求:必须是已存在的函数库函数:#include<*.h>用户自定义函数:函数类型声明函数声明一般形式:函数类型函数名(形参类型[形参名],…..);或函数类型函数名();作用:告诉编译系统函数类型、参数个数及类型,以便检验函数定义与函数声明不同函数声明位置:程序的数据声明部分(函数内或外)下列情况下,可不作函数声明若函数返值是char或int型,系统自动按int型处理被调用函数定义出现在主调函数之前有些系统(如BorlandC++)要求函数声明指出函数返值类型和形参类型,并且对void和int
型函数也要进行函数声明函数声明举例voidmain(){floata,b;intc;scanf("%f,%f",&a,&b);
c=max(a,b);printf("Maxis%d\n",c);}intmax(floatx,floaty){floatz;z=x>y?x:y;return(z);}int型函数可不作函数说明(BorlandC++不行)floatadd(floatx,floaty){floatz;z=x+y;return(z);}voidmain(){floata,b,c;scanf("%f,%f",&a,&b);
c=add(a,b);printf("sumis%f",c);}被调函数出现在主调函数之前,不必函数说明voidmain(){
floatadd(float,float);/*functiondeclaration*/
floata,b,c;scanf("%f,%f",&a,&b);c=add(a,b);printf("sumis%f",c);}floatadd(floatx,floaty){floatz;z=x+y;return(z);}可以:floatadd();不提倡。或:floatadd(floatx,floaty);例5.5【例5.5】用函数实现:求两个数的最大公约数和最小公倍数。分析问题:
最大公约数(或称最大公因子)是能够同时被两个数整除的最大数,其性质为:
①如果a>b,则a和b的最大公约数与a-b和b的最大公约数相同;②如果b>a,则a和b的最大公约数与a和b-a的最大公约数相同;③如果a=b,则a和b的最大公约数与a值和b值相同。换句话说,最大公约数是最后能使余数为0的被除数。最小公倍数为:两数相乘,再除以最大公约数。例5.5解法一(穷举法)#include<stdio.h>/*函数声明*/inthcf(intu,intv);intlcd(intu,intv,inth);intmain(){intn1,n2,h,l;scanf("%d,%d",&n1,&n2); /*输入两个数*/h=hcf(n1,n2); /*调用求最大公约数函数*/printf("greatestcommondivisor:%d\n",h);l=lcd(n1,n2,h); /*调用求最小公倍数函数*/printf("leasecommonmultiple:%d\n",l);return(0);}例5.5解法一(续)/*函数定义:求两个数的最大公约数*/inthcf(intu,intv){intt;if(v>u){t=u;u=v;v=t;}
t=v;while(u%t!=0||v%t!=0)t--;return(t);}/*函数定义:求两个数的最小公倍数,h为最大公约数*/intlcd(intu,intv,inth){return(u*v/h);}例5.5程序解法二(辗转相除法)#include<stdio.h>/*函数声明*/inthcf(intu,intv);intlcd(intu,intv,inth);intmain(){intn1,n2,h,l;scanf("%d,%d",&n1,&n2);h=hcf(n1,n2);printf("greatestcommondivisor:%d\n",h);l=lcd(n1,n2,h);printf("leasecommonmultiple:%d\n",l);return(0);}例5.5解法二(续)/*函数定义:求两个数的最大公约数*/inthcf(intu,intv){intt;if(v>u){t=u;u=v;v=t;}
while(v!=0){t=u%v;u=v;v=t;}return(u);}/*函数定义:求两个数的最小公倍数,h为最大公约数*/intlcd(intu,intv,inth){return(u*v/h);}函数的嵌套调用C程序由若干函数组成,各函数在结构上是相互平行和独立的,函数不能嵌套定义。如果不考虑函数的功能和逻辑关系,函数之间是平行的,无主从轻重,可以相互调用。C规定:函数不能嵌套定义,但可以嵌套调用。主函数可以调用自定义函数,自定义函数之间可以嵌套调用。所谓函数嵌套调用是指一个函数调用另一个函数,另一个函数再调用其他函数的层层调用方式。函数的嵌套调用关系图main()调用函数a结束函数a调用函数b函数b⑴⑵⑶⑷⑸⑹⑺⑻⑼调用函数d调用函数c函数c⑽⑾⑿⒁⒂函数d⒃⒀⒄例5.6【例5.6】使用函数的嵌套调用方法计算:分析问题:从表达式中可以看出,每项都是一样的,不同的是起止数不同,因此每项的计算可以由一个相同的函数sum()来完成。而每项中还有一个阶乘,因此还需要一个求阶乘的函数fac()。例5.6程序#include<stdio.h>doublefac(intn);doublesum(intn1,intn2);intmain(){doubles;s=sum(1,3)+sum(6,9)+sum(12,15);printf("\ns=%f",s);return(0);}/*求项的值:n1和n2为起止数*/doublesum(intn1,intn2){inti;doubles=0;for(i=n1;i<=n2;i++)s=s+1/fac(i);return(s);}例5.6程序(续)/*求n的阶乘函数*/doublefac(intn){doubles=n;if(n<=1)return1;for(;--n;)s*=n;return(s);}
main()调用函数sum结束函数fac例5.6函数嵌套调用关系用递归方法求n!值【例5.7】用递归方法求n!值。分析:使用递归方法。如计算5!可以用 5!=5×4! 4!=4×3! 3!=3×2! 2!=2×1! 1!=1其递归公式如下:n!=1当(n=0,1)//递归结束条件n×(n-1)!当(n>1)//递归方式分析问题求n!时,要先求(n-1)!;求(n-1)!时,要先求(n-2)!值;……;直到求2!要知道1!为止。这个过程称之为“回推”过程。规定1!=1;这时就可以求2!,……;当求出(n-1)!值时,就可以求出n!。这个过程称之为“递推”过程。“回推”和“递推”过程构成了“递归”问题的两个阶段。如果要求递归过程在有限步骤内,就必须有一个结束递归过程的条件。n!问题的结束条件就是1!=1,过程就是n!=(n-1)!*n。“回推”和“递推”5!5×4!4×3!3×2!2×1!15!4!×53!×42!×31!×21回推过程返回1返回1!×2=2返回2!×3=6返回3!×4=24返回4!×5=120终值120递推过程调用函数函数返回值例5.7程序#include<stdio.h>longfac(intn); /*自定义函数声明*/intmain(){intn;longr; /*定义长整型变量存放n的阶乘的值*/printf("\nInputn:");scanf("%d",&n);if(n<0) /*当n正确时求阶乘,错误时则提示*/printf("InputDataerror(n<0).\n");else{r=fac(n); /*调用求n的阶乘函数,返回r*/printf("%d!=%ld\n",n,r);/*输出n和r的值*/}return(0);}/*定义求n的阶乘的递归函数fac*/longfac(intn){longr;if(n==0||n==1)r=1;/*结束条件,其返回值是1*/elser=fac(n-1)*n;/*递归方式,返回n!值*/return(r);/*返回fac的函数值*/}函数的递归调用
在函数调用中,如果直接或间接地调用该函数本身,称为递归调用。递归有时也称为循环定义。递归又分为:直接递归调用,即函数直接调用自身。和间接调用,即函数互相调用对方。fun()调fun调g调ff()g()intfun(intx){inty,z;……
z=fun(y);…….return(2*z);}intf(intx){inty,z;……
z=g(y);…….return(2*z);}intg(intt){inta,c;……
c=f(a);…….return(3+c);}递归调用的说明上述两种递归调用都是无终止的自身调用。程序中不应出现无终止调用,而只应出现有限次数的有终止的递归调用。可以使用if语句来控制。递归程序由递归方式与递归终止条件两部分组成。即一个递归的问题可以分为:首先“回推”,然后“递推”。在递归过程中,必须具有一个结束递归过程的条件。C编译系统对递归函数的自调用次数没有限制。每调用函数一次,在内存堆栈区分配空间,用于存放函数变量、返回值等信息,所以递归次数过多,可能引起堆栈溢出。递归形式当有函数返回值时,通用的递归函数体表述如下:if(递归结束条件)return(递归公式的初值);elsereturn(递归函数调用返回的结果值);有时递归函数并不返回任何值,只是输出显示结果。有时递归子问题和原问题的形式并不完全相同,这时必须把问题作一个小的变化,使得递归子问题与原问题完全相同。例5.8【例5.8】用递归方法求解斐波纳契(Fibonacci)数列。分析问题:斐波纳契数列:0,1,1,2,3,5,8,13,21,34,…。第n项的递归公式为:fib(n)=n(n=0,1)/*递归结束条件*/fib(n-2)+fib(n-1)(n>1)/*递归方式*/第n项斐波纳契数列值是前两项的和,这是递归方式;而当n=0和1时,斐波纳契数列就是n值本身,这是递归结束条件。例5.8程序#include<stdio.h>longfib(intn);/*自定义函数声明*/voidmain(){longs;/*第i项斐波纳契数列的值*/inti=0;/*斐波纳契数列某项的序号*/do{printf("InputFibonacciNumber:");scanf("%d",&i);/*输入要求的某一项的序号*/s=fib(i);/*求斐波纳契数列第i项*/printf("Fib(%d)=%ld\n",i,s);}while(i>0);/*循环输入序号,直到i值小于等于0*/}/*定义求第n项斐波那契数列值的函数*/longfib(intn){if(n==0||n==1)/*判断是否为结束条件*/returnn;elsereturnfib(n-2)+fib(n-1);/*求斐波纳契数列的递归方式*/}变量的作用域局部变量——在语句块(一对花括号括起来的区域)内定义,仅在定义它的语句块内有效,并且拥有自己独立的存储空间全局变量——在函数之外定义的变量,可以在本文件的所有函数中使用,有效范围从定义变量的位置开始到文件结束说明在一个函数中既可以使用本函数中的局部变量,又可以使用有效的全局变量可以利用全局变量增加与函数联系的渠道,从函数得到一个以上的返回值。全局变量相当于各个函数间的传递通道为了便于区别全局变量和局部变量,C语言中有一个不成文的规定,即将全局变量名的第一个字母用大写表示编程时一般不要使用全局变量,因为全局变量一直占用存储空间,降低了函数的通用性,降低了程序的清晰性,易出错变量的存储空间变量是对程序中数据的存储空间的抽象内存…….voidmain(){inta;a=10;printf(“%d”,a);}编译或函数调用时为其分配内存单元1020002001程序中使用变量名对内存操作变量的属性变量的属性包括操作属性和存储属性。操作属性以数据类型形式表现出来。存储属性即存储器类型,它说明变量占用存储空间的区域。数据类型:变量所持有的数据的性质(操作属性)存储属性:存储器类型:寄存器、静态存储区、动态存储区生存期:变量在某一时刻存在-------静态变量与动态变量作用域:变量在某区域内有效-------局部变量与全局变量变量的作用域变量的作用域是指变量的可见范围或可使用的有效范围,即变量的“可见性”。变量的作用域可以在一个函数范围内,也可以在整个程序范围内。变量可以在程序中三个地方说明:函数内部、函数的参数定义中或所有函数的外部。函数内部定义的变量、函数的参数变量、被一对大括号{}括起来的区域(语句块)定义的变量等均为局部变量,函数外部定义的变量则为全局变量。局部变量、全局变量等的作用域是不一样的。局部变量的作用域举例#include<stdio.h>voidmain(){inta,b;…{intc;c=a+b;…}…}变量c的作用范围变量a、b的作用范围全局变量的作用域举例#include<stdio.h>floatf1(inta);/*函数声明*/intx=1,y=5;/*定义全局变量*/voidmain(){intm,n;/*定义局部变量*/m=x+10;…}floatf2(inta,intb);/*函数声明*/floatf1(inta)/*定义函数,函数参数是局部变量*/{intb,c;/*定义局部变量*/…}charc1,c2;/*定义全局变量*/floatf2(inta,intb)/*定义函数,函数参数是局部变量*/{inti,j,x;/*定义局部变量*/x=6;…}floats1,s2;/*定义全局变量*/全局变量c1、c2的作用范围全局变量x、y的作用范围例5.9【例5.9】输入正方体的长l、宽w、高h,求体积及三个不同面的面积。分析问题:可以定义s1、s2、s3三个全局变量,分别表示三个不同面的面积,并在求体积vs()函数中一并计算,然后在主函数main()中输出。例5.9程序#include<stdio.h>ints1,s2,s3;/*定义全局变量s1、s2、s3*/intvs(inta,intb,intc);/*自定义函数声明*/intmain(){intv,l,w,h;/*定义局部变量v、l、w、h*/printf("\ninputlength,widthandheight\n");scanf("%d,%d,%d",&l,&w,&h);v=vs(l,w,h);printf("v=%d,s1=%d,s2=%d,s3=%d\n",v,s1,s2,s3);return(0);}intvs(inta,intb,intc){intv;/*在vs()函数中定义局部变量v*/v=a*b*c;s1=a*b;/*自定义函数中引用s1、s2、s3*/s2=b*c;s3=a*c;return(v);}变量的存储类型从变量的作用域角度来分,变量可以分为全局变量和局部变量而从变量值存在的时间(即生存期)角度来分,变量可以分为静态存储方式和动态存储方式静态存储方式是指程序运行期间分配固定的存储空间,该空间从分配时刻开始存在,直到程序运行结束才释放动态存储方式是指程序运行期间根据需要动态分配存储空间,该空间使用结束后自动释放,下次需要时再分配存储类型的提出下面程序的最后定义全局变量A、B有意义吗?要使它能使用,必须通过通过externintA,B;语句扩展其作用域。这就是变量的存储类型所要做的事情(参见例5.11)。#include<stdio.h>intmax(intx,inty){intz;z=x>y?x:y;return(z);}voidmain(){printf("max=%d",max(a,b));}intA=13,B=-8;externintA,B;变量A、B本程序中无效变量A、B的有效范围存储类型的分类与定义变量的存储类型分类auto-----自动型register-----寄存器型static------静态型extern-----外部型变量定义格式:[存储类型]数据类型变量表;如:intsum;
autointa,b,c;
registerinti;
staticfloatx,y;动态变量与静态变量动态变量与静态变量,是从变量的生存期角度来分类的。1.存储方式静态存储:程序运行期间分配固定存储空间。动态存储:程序运行期间根据需要动态分配存储空间。2.内存用户区程序区静态存储区动态存储区全局变量、局部静态变量形参变量局部动态变量(autoregister)函数调用现场保护和返回地址等3.生存期静态变量:从程序开始执行到程序结束。动态变量:从包含该变量定义的函数开始执行至函数执行结束。自动变量auto在C语言中,函数内部定义的变量(局部变量)默认为自动变量(automaticvariable)。关键字auto能够显式地指定存储类别。显式定义
autointx,y;隐含定义等价intx,y;关键字“auto”可以省略,auto不写则隐含确定为“自动存储类别”,即动态存储方式。程序中大多数变量属于自动变量。auto的作用域main(){intx=1;voidprt(void);{intx=3;prt();printf(“2ndx=%d\n”,x);}printf(“1stx=%d\n”,x);}voidprt(void){intx=5;printf(“3thx=%d\n”,x);}运行结果:3thx=52ndx=31stx=1x=1作用域x=1作用域x=3作用域x=5作用域寄存器变量register
对于使用频繁的变量,可以使用register声明为寄存器变量,其值存储在CPU中,加快了运行速度。如:registerintx;registercharc;说明:①只有局部自动变量和形式参数可以作为寄存器变量,其余非法。②一个计算机系统中的寄存器数目是有限的,不能定义任意多个寄存器变量。③一般来讲只允许int和char才能定义为寄存器变量,但目前大多数系统都支持指针型变量定义为寄存器变量。④局部静态变量不能定义为寄存器变量,即不能定义:registerstaticintx;,对一个变量只能声明一个存储类别。⑤当今优化的编译系统,能够自动识别使用频繁的变量,从而自动的将这些变量放在寄存器中。因此程序设计者实际上根本不需要去声明寄存器变量。用static声明局部变量
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,以便下一次调用该函数时可以使用上一次调用的最后结果。这时就应该指定该局部变量为“静态局部变量”。#include<stdio.h>voidmain(){voidincrement(void);increment();increment();increment();}voidincrement(void){intx=0;x++;printf(“%d\n”,x);}运行结果:111#include<stdio.h>voidmain(){voidincrement(void);increment();increment();increment();}voidincrement(void){staticintx=0;x++;printf(“%d\n”,x);}运行结果:123例:局部静态变量值具有可继承性例5.10【例5.10】用静态变量实现计算1~5的阶乘的程序。分析问题:
n!=1*2*…*(n-1)*n,可以依次计算1!=1,2!=1!*2,3!=2!*3,…,n!=(n-1)!*n。也就是说,计算n!需要使用前面的(n-1)!值,而(n-1)!值可以使用静态变量来存储。例5.10程序#include<stdio.h>intfac(inti);/*计算阶乘的函数声明*/intmain(){inti,r;/*如果i值频繁使用,编译器会自动以寄存器变量存放*/for(i=1;i<=5;i++){r=fac(i);/*调用计算i的阶乘函数*/printf("%d!=%d\n",i,r);}return(0);}/*定义计算i的阶乘的函数*/intfac(inti){staticintf=1;/*定义静态变量f存放阶乘值,第一次调用时赋初值*/if(i>=1)f=f*i;/*f的初值为上次计算的f值*/return(f);}用extern声明外部变量外部变量(即全局变量)是在函数的外部定义的,它的作用域为从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。有时需要用extern来声明外部变量,以扩展外部变量的作用域。如果全局变量在后面定义,而在前面的函数中要使用,则必须在使用前用extern声明该全局变量。如:externintAbc;
也可以省略int成:externAbc;全局变量的定义与使用extern声明的主要区别
项目全局变量的定义使用extern声明次数只能1次可声明多次位置所有函数之外函数内或函数外分配内存分配内存
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 幼儿园水电安全培训
- 消防与家庭安全
- 宁夏回族自治区银川市西夏区2024-2025学年二年级上学期期中考试数学试题
- 广东省惠州市惠州中学2024-2025学年高二上学期11月期中考试政治试题 - 副本
- 2024-2025学年宁夏吴忠市青铜峡市宁朔中学高一(上)第一次月考生物试卷(含答案)
- 2024-2025学年初中九年级数学上册第一次月考模拟卷含答案解析
- 中考英语语法串串烧十大状语从句
- 优化方案2011高考总复习地理一轮复习课件大气的受热过程气压带和风带湘教版
- 固定资产投资专项审计
- 出纳年度工作计划范文-文书模板
- 胃癌淋巴结清扫ppt课件(PPT 39页)
- 汽车交货方案及质保措施
- 06竣工财务决算审计工作底稿(试行)
- 某公司审计财务舞弊案例分析报告
- 人教版九年级初三上册期中考试化学试卷
- 电加热管制作工艺的设计
- 双港垃圾焚烧发电厂工艺介绍
- 植物体的结构层次通用课件
- 编带包装检验判定标准
- 新译林版六年级上册英语知识点归纳总结
- lovestory(爱情故事)歌词中英文对照
评论
0/150
提交评论