程序设计基础C语言第7章 函数

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第7章函数内容提要函数定义、函数调用、函数原型、函数的参数传递与返回值递归函数和函数的递归调用函数封装,函数复用,函数设计的基本原则,程序的健壮性变量的作用域与存储类型,全局变量、自动变量、静态变量、寄存器变量“自顶向下、逐步求精”的模块化程序设计方法数学中的函数()yfx自变量因变量函数名程序设计中的函数程序设计中的函数不局限于计算–计算类,如打印阶乘表的程序……–判断推理类,如排序、查找……参数返回值问题的提出读多少行的程序能让你不头疼?假如系统提供的函数printf()由10行代码替换,那么你编过的程序会成什么样子?实际上一个printf()有上千行代码main()中能放多少行代码?如果所有代码都在main()中,怎么团队合作?如果代码都在一个文件中,怎么团队合作?“事无巨细”,“事必躬亲”X7.1分而治之与信息隐藏分而治之(DivideandConquer,Wirth,1971)把较大的任务分解成若干个较小的任务,并提炼出公用任务:函数信息隐藏(InformationHiding,Parnas,1972)设计得当的函数可把具体操作细节对程序中无需知道它们的那些部分隐藏掉,从而使整个程序结构清楚使用函数时,不用知道函数内部是如何运作的,只按照我们的需要和它的参数形式调用它即可7.2函数(function)的定义函数是模块化编程的最小单位若干相关的函数可以合并成一个“模块”一个C程序由一个或多个源程序文件组成一个源程序文件由一个或多个函数组成7.2.1函数的分类函数生来都是平等的,互相独立的,没有高低贵贱和从属之分main()稍微特殊一点点C程序的执行从main函数开始调用其他函数后流程回到main函数在main函数中结束整个程序运行7.2.1函数的分类标准库函数ANSI/ISOC定义的标准库函数符合标准的C语言编译器必须提供这些函数函数的行为也要符合ANSI/ISOC的定义第三方库函数由其它厂商自行开发的C语言函数库不在标准范围内,能扩充C语言的功能(图形、网络、数据库等)自定义函数自己定义的函数包装后,也可成为函数库,供别人使用7.2.2函数定义(Functiondefinition)类型函数名(类型参数1,类型参数2,……){声明语句序列可执行语句序列return表达式;}返回值类型函数名标识符,说明运算规则参数表相当于运算的操作数返回运算的结果函数出口7.2.2函数定义(Functiondefinition)类型函数名(类型参数1,类型参数2,……){声明语句序列可执行语句序列return表达式;}函数体的定界符参数表里的变量(叫形式参数,FormalParameter)也是内部变量函数体7.2.2函数定义(Functiondefinition)void函数名(void){声明语句序列可执行语句序列return;}函数无返回值,用void定义返回值类型用void定义参数,表示没有参数return语句后无需任何表达式【例7.1a】计算整数n的阶乘n!/*函数功能:用迭代法计算n!函数入口参数:整型变量n表示阶乘的阶数函数返回值:返回n!的值*/longFact(intn)/*函数定义*/{inti;longresult=1;for(i=2;i=n;i++){result*=i;}returnresult;}返回值类型函数名说明函数的功能返回值作为函数调用表达式的值形参表,函数入口函数内部可以定义只能自己使用的变量,称内部变量函数名(表达式1,表达式2,……);实际参数(ActualArgument)函数调用(FounctionCall)时提供的表达式中的参数有返回值时放到一个数值表达式中c=max(a,b);作为另一个函数调用的参数c=max(max(a,b),c);printf(%d\n,max(a,b));无返回值时函数调用表达式display(a,b);返回值=函数名(实参表列);函数名(实参表列);7.3向函数传值和从函数返回值函数的参数传递实参和形参必须匹配数目一致,类型一一对应(否则会发生自动类型转换)【例7.1】例:交换两个数据。#includestdio.hvoidmain(){voidswap(int,int);inta,b;a=2,b=6;swap(a,b);printf(“a=%d,b=%d”,a,b);}a2b6voidswap(intx,inty){inttemp;temp=x;x=y;y=temp;}x2y662a、b并未被交换。函数的参数和返回值实参和形参必须类型匹配形参也是一个变量,在函数被调用的时候分配空间当发生函数调用的时候,实参的值传递给形参通过返回值函数将值传回给调用的函数,返回值的类型由函数的返回类型决定一个函数最多只能有一条return语句例:四舍五入#includestdio.hintround(floatx){x=x+0.5;returnx;}voidmain(){floata=2.7,b=3.4;inti,j;i=round(a);j=round(b);printf(“%i,%i\n”,i,j);}ij2.73.4ab2.7x3.23.43.9337.3.2函数原型(FunctionPrototype)在调用函数前先声明其返回值类型、函数名和参数函数原型有助于编译器对函数参数类型的匹配检查末尾有一个分号,声明时不要省略形参和返回值的类型【例7.1】函数定义与函数声明的区别函数定义指函数功能的确立指定函数名、函数类型、形参及类型、函数体等是完整独立的单位函数声明是对函数名、返回值类型、形参类型的说明不包括函数体是一条语句,以分号结束,只起一个声明作用7.3.3函数封装(Encapsulation)函数封装使得外界对函数的影响仅限于入口参数,而函数对外界的影响仅限于一个返回值和数组、指针类型的参数【例7.1】Why?传入负数的实参会怎样?#includestdio.hlongFact(intn);voidmain(){intm;longret;printf(inputm:);scanf(%d,&m);ret=Fact(m);printf(%d!=%ld\n,m,ret);}longFact(intn){inti;longresult=1;for(i=2;i=n;i++){result*=i;}returnresult;}防御性程序设计(DefensiveProgramming)如何使函数具有遇到不正确使用或非法数据输入时避免出错的能力,增强程序的健壮性?在函数的入口处,检查输入参数的合法性【例7.2】计算整数n的阶乘n!longFact(intn){inti;longresult=1;if(n0){printf(Inputdataerror!\n);}else{for(i=2;i=n;i++)result*=i;returnresult;}}如何使函数具有遇到不正确使用或非法数据输入时避免出错的能力,增强程序的健壮性?在函数的入口处,检查输入参数的合法性防御性程序设计(DefensiveProgramming)【例7.2】计算整数n的阶乘n!主函数如何修改?增加对函数返回值的检验防御性程序设计(DefensiveProgramming)【例7.3】计算整数n的阶乘n!传入负数的实参时Fact()会返回-1吗?存在死代码的原因何在?防御性程序设计(DefensiveProgramming)【例7.3】计算整数n的阶乘n!如何修改这个缺陷?如何保证不会传入负数实参?防御性程序设计(DefensiveProgramming)【例7.2】计算整数n的阶乘n!【例7.4】编写计算组合数的程序函数复用7.3.4函数设计的基本原则信息隐藏1函数规模要小2函数功能要单一3函数接口定义要清楚入口参数有效性检查敏感操作前的检查调用成功与否的检查函数的嵌套调用嵌套调用–在调用一个函数的过程中,又调用另一个函数C语言规定函数不能嵌套定义,但可以嵌套调用–函数是相互平行的main(){……a();}a函数{b();…return;}b函数{……return;}①③④⑤⑥⑦②7.4递归函数(RecursionFunction)7.4.1递归的概念在函数当中又出现直接或者间接的调用该函数本身——c的一个特色直接递归:fun1(){…fun1();…}间接递归:fun1(){…fun2();…}fun2(){…fun1();…}例:有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人多大。age(5)=age(4)+2age(4)=age(3)+2age(3)=age(2)+2age(2)=age(1)+2age(1)=10用数学公式表述如下:age(n)=10(n=1)age(n)=age(n-1)+2(n1)intage(intn)/*求年龄的递归函数*/{intc;/*c用作存放函数的返回值的变量*/if(n==1)c=10;elsec=age(n-1)+2;return(c);}运行结果如下:18#includestdio.hvoidmain(){printf(“%d”,age(5));}7.4.2递归调用(RecursiveCall)longfact(intn){if(n0)return-1;elseif(n==0||n==1)return1;elsereturnn*fact(n-1);}【例7.6】计算n!=n*(n-1)*(n-2)*…*1函数直接或间接调用自己无需考虑n0了7.4.2递归调用(RecursiveCall)unsignedlongfact(unsignedintn){if(n==0||n==1)return1;elsereturnn*fact(n-1);}基线情况(basecase)一般情况(generalcase)【例7.6】计算n!=n*(n-1)*(n-2)*…*1递归调用其实是一种算法问题的解决需要通过有规律的调用同样的算法直到某些已知值或直接值通常可以抽象为函数:f(x)=a,c=truef(x’),c=false{7.4.2递归调用(RecursiveCall)递归调用应该能够在有限次数内终止递归递归调用若不加以限制,将无限循环调用必须在函数内部加控制语句,仅当满足一定条件时,递归终止,称为条件递归任何一个递归调用程序必须包括两部分递归循环继续的过程递归调用结束的过程if(递归终止条件成立)return递归公式的初值;elsereturn递归函数调用返回的结果值;n!=n*(n-1)!(n-1)!=(n-1)*(n-2)!(n-2)!..(n-3)!5!:4!=4*3!3!=3*2!2!=2*1!1!=1回推过程递推过程每个递归函数必须至少有一个基线条件一般情况必须最终能简化为基线条件递归层数太多易导致栈空间溢出后果很严重,程序被异常中止fact(5)=5*fact(4)=120fact(4)=4*fact(3)=24fact(3)=3*fact(2)=6fact(2)=2*fact(1)=2fact(1)=1mainfact(5)fact(4)fact(3)fact(2)fact(1)递归与迭代迭代即循环方法来编写的阶乘函数unsignedlongFact(unsignedintn){unsignedlongresult=1;unsignedinti;for(i=1;i=n;i++)result*=i;returnresult;}递归程序遵循了数学中对阶乘的定义因此递归方法编写程序具有更清晰、可读性更好的优点递归与迭代1,1,2,3,5,8,......110)2()1(10)(

1 / 79
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功