版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第六章函数目录/Contents函数定义、函数调用、函数原型、函数的参数传递与返回值函数封装,函数复用,函数设计的基本原则,程序的健壮性递归函数和函数的递归调用变量的作用域与存储类型,全局变量、自动变量、静态变量、寄存器变量模块化程序设计方法◆当一个程序的代码量达到数千行以上时,所有的代码都放在main函数中吗?如何进行团队合作?◆所有代码放在一个文件中,如何进行团队合作?◆调试良好的代码如何被其他程序使用呢?◆当一个问题的规模很大时,应该采用什么样的方法来解决它呢?问题的提出模块化程序设计思想分而治之函数把较大的任务分解成若干个较小的任务,并提炼出公用任务信息隐藏设计良好的函数可把具体操作细节对外界隐藏起来,从而使整个程序结构清楚使用函数时,用户不用知道函数内部是如何运作的,只按照我们的需要和它的参数形式调用它即可函数的定义■函数是C语言中模块化程序设计的最小单位,用来实现各种不同的功能。每个函数可看作是一个程序模块。■模块化程序设计如同制造机器,函数相当于机器的“零部件”
-先将这些“零部件”单独设计、调试和测试好,接着进行组装,最后进行综合测试。
-这些“零部件”既可以是自己设计的,也可以是别人设计好的,还可以是现成的标准产品。■若干个函数可以合成一个“模块”■一个C程序由一个或若干个源文件组成■一个源文件由一个或多个函数组成函数的定义■一般来说,函数是各自独立、平等的,没有高低贵贱之分。
-main()稍稍特殊一点,
-程序的执行从main()的入口地址开始-在函数体中顺序、循环、递归、迭代地调用各个函数,调用其他函数后程序执行流程返回main函数
-在main()的出口结束程序的运行函数的分类■标准库函数
-ANSI/ISOC定义的标准库函数符合标准的C语言编译器必须提供这些函数函数的行为也要符合ANSI/ISOC的定义-第三方库函数由其他厂商自行开发的C语言函数库不在标准范围内,能扩充C语言的功能(图形、网络、数据库等)■自定义函数
-自己定义的函数包装后,也可成为函数库,供别人使用函数的分类返回值类型
函数名(类型形式参数1,类型形式参数2,…)←函数头部{
声明语句序列
执行语句序列}函数的定义函数体函数体的定界符void函数名(void)←函数头部{
声明语句序列
执行语句序列}函数体函数定义【例6.1】
编写一个函数计算整数x的n次幂
。/*函数功能:用迭代法计算x的n次幂。
函数参数:整型变量x表示底数,整型变量n表示幂指数。
函数返回值:返回x的n次幂的值。*/longPower(intx,intn) /*函数定义*/{ inti; longresult=1; for(i=0;i<n;i++) { result=result*x;/*计算x的n次幂*/ } returnresult;/*将result的值作为函数的返回值返回*/}返回值类型函数名说明函数的功能形参表,函数入口函数内部可以定义只能自己使用的变量,称内部变量返回值作为函数调用表达式的值函数调用◆有返回值时:◆无返回值时函数名(实参表列);
返回值=函数名(实参表列);函数调用#include<stdio.h>intmain(){intp,q;longret;printf("pleaseinputtwointegers:");scanf("%d%d",&p,&q);ret=Power(p,q);printf("ret=%ld",ret);return0;}函数原型声明#include<stdio.h>longPower(intx,intn);intmain(){intp,q;longret;printf("pleaseinputtwointegers:");scanf("%d%d",&p,&q);ret=Power(p,q);printf("ret=%ld",ret);return0;}末尾有一个分号,声明时不要省略形参和返回值的类型longPower(intx,intn){ inti; longresult=1; for(i=0;i<n;i++) { result=result*x; } returnresult;}函数定义函数定义指函数功能的确立指定函数名、函数类型、形参及类型、函数体等是完整独立的单位
函数声明是对函数名、返回值类型、形参类型的说明不包括函数体是一条语句,以分号结束,只起一个声明作用函数声明与函数定义的区别函数封装与防御性编程■函数封装(Encapsulation)使得外界对函数的影响仅限于入口参数,而函数对外界的影响仅限于一个返回值。【例6.2】
试编写一个函数,从键盘输入整数n,计算n!,并编写测试程序。longFac(intn){ inti; longresult=1; for(i=2;i<=n;i++) { result=result*i; } returnresult;}#include<stdio.h>longFac(intn);intmain(){ intm; longret; printf("pleaseinputm:"); scanf("%d",&m); ret=Fac(m); /*调用阶乘计算函数*/ printf("%d!=%ld",m,ret); /*输出语句*/ return0;}防御性编程■函数具有遇到不正确使用或非法数据输入时保护自己避免出错的能力。在函数的入口处增加对入口参数的合法性检查,是一种增强程序健壮性的有效方法。/*用迭代法计算n的阶乘*/longFac(intn){ inti; longresult=1; if(n<0) { printf("inputdataerror!\n"); } else { for(i=2;i<=n;i++) { result=result*i; } returnresult; }}#include<stdio.h>longFac(intn);intmain(){ intm; longret; printf("pleaseinputm:"); scanf("%d",&m); ret=Fac(m); if(ret==-1) printf("inputdataerror!\n"); else printf(“%d!=%ld",m,ret); return0;}/*用迭代法计算n的阶乘*/longFac(intn){ inti; longresult=1; if(n<0) { return-1; } else { for(i=2;i<=n;i++) { result=result*i; } returnresult; }}防御性编程#include<stdio.h>unsignedlongFac(unsignedintn);intmain(){ intm; longret; printf("pleaseinputm:"); scanf("%d",&m); ret=Fac(m); if(ret==-1) printf("inputdataerror!\n"); else printf(“%d!=%ld",m,ret); return0;}/*用迭代法计算n的阶乘*/unsignedlongFac(unsignedintn){ unsignedinti; unsignedlongresult=1; if(n<0) { return-1; } else { for(i=2;i<=n;i++) { result=result*i; } returnresult; }}传入负数的实参时Fac()会返回-1吗?存在死代码的原因何在?防御性编程如何修改程序去除冗余代码?如何保证不会传入负数实参?#include<stdio.h>unsignedlongFac(unsignedintn);intmain(){ intm; longret; printf("pleaseinputm:"); scanf("%d",&m); ret=Fac(m); if(ret==-1) printf("inputdataerror!\n"); else printf(“%d!=%ld",m,ret); return0;}unsignedlongFac(unsignedintn){ unsignedinti; unsignedlongresult=1; for(i=2;i<=n;i++) { result=result*i; } returnresult;}运行结果:pleaseinputm(m>0):-1pleaseinputm(m>0):55!=120防御性编程【例6.3】
编写程序,实现组合数的计算。unsignedlongFac(unsignedintn){ unsignedinti; unsignedlongresult=1; for(i=2;i<=n;i++) { result=result*i; } returnresult;}#include<stdio.h>unsignedlongFac(unsignedintn);intmain(){intm,k;unsignedlongret;do{printf("pleaseinputm,k(m>=k>0):");scanf("%d%d",&m,&k);}while(m<k||m<=0||k<0);ret=Fac(m)/(Fac(k)*Fac(m-k));printf("ret=%lu\n",ret);return0;}函数设计原则①函数的规模要小;②函数的功能要单一,不要设计多功能的函数;③在函数的接口中清楚地定义函数的行为;④在函数的入口处,对参数的有效性进行检查;⑤应检查某些敏感性操作;⑥不能主观地认为调用一个函数总会成功,要考虑如果调用失败,应该如何处理。函数嵌套调用嵌套调用:在调用一个函数的过程中,又调用另一个函数C语言规定函数不能嵌套定义,但可以嵌套调用,函数是相互平行的递归函数■目标:将A上的盘子全部移到B上■规则:每次只能移动一个盘子,不允许大盘子放在小盘子上第一步:A→B
第二步:A→CABCn=3【例6.4】汉诺塔问题递归函数ABC
第三步:B→C第四步:A→Bn=3递归函数ABC
第五步:C→A第六步:C→Bn=3递归函数ABCn更大些怎么办?
第七步:A→Bn=3递归函数第1步:将问题简化假设A杆上只有2个圆盘,即汉诺塔有2层,n=2将1号圆盘从A移到C将2号圆盘从A移到B将1号圆盘从C移到BABC第2步:对于一个有n(n>1)个圆盘的汉诺塔,将n个圆盘分为两部分:上面n-1个圆盘和最下面的n号圆盘。将“上面n-1个圆盘”看成一个整体将n-1个圆盘从A移到C将n号圆盘从A移到B将n-1个圆盘从C移到BACB递归函数递归函数设计2个函数:将n个圆盘借助C从A移到B
将一个圆盘从A移到B
ACB将n-1个圆盘从A移到C将n号圆盘从A移到B将n-1个圆盘从C移到BHanoi(intn,chara,charb,charcMove(intn,chara,charb);step1Hanoi(n-1,a,c,b)step2Move(n,a,b)Step3Hanoi(n-1,c,b,a)递归函数递归方法的基本原理将一个复杂问题逐步分解并最终简化为一个显而易见的小问题这个小问题的解决,意味着整个复杂问题的解决
递归函数递归函数【例6.5】
编写程序,用递归法计算整数x的n次幂xn。longPower(intx,intn) /*函数定义*/{if(n==0)return1;elsereturnx*Power(x,n-1);}一般情况基线情况递归函数递归调用应该能够在有限次数内终止递归递归调用若不加以限制,将无限循环调用必须在函数内部加控制语句,仅当满足一定条件时,递归终止,称为条件递归任何一个递归调用程序必须包括两部分递归循环继续的过程递归调用结束的过程
if(递归终止条件成立)
return递归公式的初值;elsereturn递归函数调用返回的结果值;
递归函数每个递归函数必须至少有一个基线条件一般情况必须最终能简化为基线条件递归层数太多易导致栈空间溢出后果很严重,程序被异常中止优点:从编程角度来看,比较直观、精炼,逻辑清楚符合人的思维习惯,逼近数学公式的表示尤其适合非数值计算领域hanoi塔,骑士游历、八皇后问题(回溯法)缺点:增加了函数调用的开销,每次调用都需要进行参数传递、现场保护等耗费更多的时间和栈空间应尽量用迭代形式替代递归形式递归与迭代的比较变量的作用域与存储类型什么是变量的作用域?指在源程序中定义变量的位置及其能被读写访问的范围分为局部变量全局变量局部变量指在函数或语句块内定义的变量形参也是局部变量特点生存期是该语句块,进入语句块时获得内存,仅能由语句块内语句访问,退出语句块时释放内存,不再有效定义时不会自动初始化,除非程序员指定初值并列语句块各自定义的同名变量互不干扰形参和实参可以同名在所有函数之外定义的变量生存期是整个程序,从程序运行起占据内存,程序运行过程中可随时访问,程序退出时释放内存有效范围是从定义变量的位置开始到本程序结束全局变量变量的存储类型指数据在内存中存储的方式即编译器为变量分配内存的方式,它决定变量的生存期变量定义格式:存储类型数据类型变量名;C语言的存储类别auto型(自动变量)static型(静态变量)extern型(外部变量)register型(寄存器变量)变量的存储类型■变量的生成期:决定何时“生”,何时“灭”静态存储区中的变量:与程序“共存亡”动态存储区中的变量:与所在函数“共存亡”寄存器中的变量:同动态存储区自动变量与静态变量auto
数据类型变量名;auto体现在进入函数/语句块时自动申请内存,退出时自动释放内存动态局部变量,缺省的存储类型静态变量static
数据类型变量名;在语句块/函数内声明的静态局部变量,其生存期为整个程序生存期为整个程序运行期间自动变量与静态变量#include<stdio.h>intf(inta);intmain(){ inta=2,i; for(i=0;i<3;i++) { printf("%d\t",F(a)); } printf("\n"); return0;}intF(inta){ autointb=0; staticintc=3; b++; c++; returna+b+c;}静态变量仅初始化一次,变量的值可保存到下次进入函数,使函数具有记忆功能运行结果:789自动变量与静态变量#include<stdio.h>intf(inta);intmain(){ inta=2,i; for(i=0;i<3;i++) { printf("%d\t",F(a)); } printf("\n"); return0;}intF(inta){ autointb=0; intc=3; b++; c++; returna+b+c;}运行结果:777静态局部变量和全局变量自动初始化为0值。自动变量不初始化时,值是随机值寄存器变量寄存器CPU内部容量有限、但速度极快的存储器
register
类型名变量名;
使用频率比较高的变量声明为register
,可使程序更小、执行速度更快现代编译器有能力自动把普通变量优化为寄存器变量,并且可以忽略用户的指定所以一般无需特别声明变量为register
变量的作用域与存储类型小结全局变量静态外部变量(只限本文件使用)外部变量
(非静态外部变量允许其他文件引用)局部变量
自动变量,(离开函数,值就消失)寄存器变量(离开函数,值就消失)定义点之前使用,需用extern声明静态局部变量(离开函数,值仍保留)动态局部变量模块化程序设计功能分解自顶向下、逐步求精的过程模块分解的基本原则保证模块的相对独立性——高聚合、低耦合模块的实现细节对外不可见——信息隐藏外部:关心做什么;内部:关心怎么做设计好模块接口接口指罗列出一个模块的所有的与外部打交道的变量等定义好后不要轻易改动在模块开头(文件的开头)进行函数声明模块化程序设计逐步求精(StepwiseRefinement)由不断的自底向上修正所补充的自顶向下(Top-down)的程序设计方法模块化程序设计模块各司其职每个模块只负责一件事情,它可以更专心便于进行单个模块的设计、开发、调试、测试和维护等工作一个模块一个模块地完成,最后再将它们集成开发人员各司其职按模块分配任务,职责明确并行开发,缩短开发时间什么时候需要模块化?某一功能,如果重复实现3遍以上,即应考虑模块化,将它写成通用函数,并向小组成员发布模块化程序设计模块化的优点——复用构建新的软件系统可以不必每次从零做起,直接使用已有的经过反复验证的软构件,组装或加以合理修改后成为新的系统,提高软件生产率和程序质量在其他函数的基础上构造程序拿来拿去主义,指尽可能复用其他人现成的模块不是人类懒惰的表现,而是智慧的表现一般要靠日积月累才能建立可以被复用的软件库前期投入多,缺乏近期效益,大部分公司都注重近期效益,是为了生存,所以软件复用对公司来说不是最高优先级【例6.6】模块化设计抛硬币游戏猜硬币正反面,10次猜不对就猜下一个数模块分解过程开始结束初始化退出处理主功能为程序运行所做的准备工作在退出前要做的事情,如打印结果、资源释放等自底向上自顶向下的模块化程序设计模块化设计抛硬币游戏开始结束生成硬币猜正反面intmain(){intcoin;charreply;srand((unsigned)time
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 17001.8-2024防伪油墨第8部分:防涂改防伪油墨
- GB/T 28589-2024地理信息定位服务
- 交通工程监理合同管理资料范本2
- 眼科检查设备项目运营指导方案
- 助听器验配服务行业相关项目经营管理报告
- 哇音踏板产品供应链分析
- 室内电烤架产品供应链分析
- 倾卸式斗车产业链招商引资的调研报告
- 人工智能技术领域的研究行业营销策略方案
- 瑜伽上衣项目运营指导方案
- 2024秋期国家开放大学《政治学原理》一平台在线形考(形考任务三)试题及答案
- 化工企业中试阶段及试生产期间的产品能否对外销售
- DBJ∕T 15-138-2018 建筑电气防火检测技术规程
- 北师大版数学七年级上册期中测试题【含答案】(共4套)
- 2022年WABCO防抱死系统
- 离婚登记申请受理回执单(民法典版)
- 《过敏性休克》PPT课件(PPT 32页)
- 金属风管支架重量计算表
- 《幼儿园中班第一学期家长会》 PPT课件
- 企业员工职业生涯规划表模板
- 电子档案管理系统需求
评论
0/150
提交评论