《(17)--第07章C语言程序设计函数.ppt》由会员分享,可在线阅读,更多相关《(17)--第07章C语言程序设计函数.ppt(91页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第7章 函数本章内容7.1求素数7.2俄罗斯方块的随机显示7.3汉诺塔7.4明文与密文7.5内部函数与外部函数本章学习目标掌握函数的定义方法熟悉函数声明的作用与方法掌握函数的调用方法掌握变量、函数的作用域和生命周期掌握函数调用时参数传递的特点掌握递归函数的定义和使用获得运用函数解决实际复杂问题的能力学习导引C语言的函数通常分为库函数与自定义函数C语言提供了丰富的库函数,但应用程序核心功能仍由自定义函数实现函数是程序的基本元素函数使程序的层次结构更清晰函数使程序更容易编写、阅读和调试7.1 求素数函数的引入、定义、参数、返回值、函数申明与调用7.1 求素数以方法3为例,以下代码可以打印10020
2、0之间的所有素数:for(k=100;k200;k=k+1)/*打印出100-200之间的素数*/m=(int)sqrt(k);/*求出k的平方根,再取其整数部分*/for(i=2;im)/*2m之间数都不是k的约数,则k是一个素数*/printf(%5d,k);例例7.17.1编程打印出100200,300600以内的所有素数。分析:打印300600中的素数求解法与100200中素数求解法本质上并无差别,只需要将外循环中k的初值与终值修改成对应值即可。所以,可以采用复制上述代码,修改循环初值与终值的方式来解决问题。这种方法有几个不足:代码完全复制,使得源文件变长,执行文件变得臃肿;复制代码后
3、要及时更换所使用的循环变量初值与终值,否则会出错。函数解决方案:观测这两段代码for(k=101;k200;k=k+1)for(i=2;i=m;i+)if(k%i=0)break;for(k=300;k600;k=k+1)100200将变化的两处地方用两个变量:start,end来代替,上述代码就可以改造成一个函数sectionPrime。void sectionPrime(int start,int end)int i,m,k;for(k=start;kend;k=k+1)/*打印出start-end之间的素数*/m=(int)sqrt(k);for(i=2;i m)printf(%5d,k
4、);printf(n);有了sectionPrime函数,主函数就变得更简单:int main()int i,m,k;sectionPrime(100,200);/*打印出300-600之间的素数*/sectionPrime(300,600);/*打印出300-600之间的素数*/return 0;源程序例例7.2 7.2 从键盘输入若干个大于1的正整数,输出其中的素数,输入负数时结束。分析:1.输入一个数number2.如果number0,退出3.如果number是素数,输出4.转到1.可以设计一个函数:isprime(intx)用来表示判断整数x是否为素数,如果x是素数,isprime(x
5、)的值为1,否则为0。则主函数可以表示如下:intmain()intnumber;intisprime(intx);/*函数声明*/printf(输入数据:n);while(1)。while(1)scanf(%d,&number);/*输入一个数number*/if(number0)/*输入的是负数时,退出*/return0;if(isprime(number)/*如果number是素数,输出*/printf(%3d是素数n,number);/*继续输入下一个数*/isprime函数怎么设计?怎么判断N是素数?所有比1大的整数中,除了1和它本身以外,没有别的约数,这种整数叫做质数或素数。int
6、isprime(intx)/*函数的定义*/inti;for(i=2;i1);/*如果x在2x的平方根之中没有约数,x1时,则x是素数*/*x小于等于1的时候,根据定义x不是素数*/源程序自定义函数:1.函数定义函数定义指明了一个函数的名字、函数的返回值类型、函数的参数列表和函数的功能代码所有函数定义都是平行的,在一个函数的函数体内,不能再定义另一个函数2.函数声明函数声明位于函数的调用之前指出一个函数的名称、函数的返回值类型、函数参数的类型函数的声明要与函数的定义相容函数的定义如果出现在函数的调用之前,则可以省去函数声明自定义函数:3.函数调用函数之间允许相互调用,也允许嵌套调用程序的执行总
7、是从main函数开始一个程序有且仅有一个主函数main7.1.1 函数的定义有参函数的定义有参函数定义的一般形式为:函数类型函数名(形式参数列表)声明部分;语句序列;7.1.1 函数的定义7.1.1 函数的定义无参函数的定义无参函数定义的一般形式为:函数类型函数名()声明部分;语句序列;无参函数的定义无参函数定义的一般形式为:函数类型函数名(void)声明部分;语句序列;例如:voidhello(void)printf(Hello,worldn);7.1.2函数的参数和函数的值形参和实参函数定义中的参数称为形参(又称形式参数)函数调用时填写的参数称为实参(又称实际参数)发生函数调用时,主调函数
8、把实参的值传送给被调函数的形参变量,从而实现主调函数向被调函数的数据传送。7.1.2函数的参数和函数的值函数的形参和实参具有以下特点:1.形参变量在每次函数被调用时分配存储空间,在调用结束时,由系统释放所分配的内存单元。2.实参都必须具有确定的值,以便把这些值传送给形参3.实参和形参的数量必须严格一致,对应位置的参数在数据类型上应兼容4.函数调用中发生的数据传送是单向的值传递例7.3形参与实参所占用的内存单元不同。#include void para_adress(int x)printf(形参x的地址:=0X%X;x=%dn,&x,x);int main()int i=10;printf(实
9、参i的地址:=0X%x;i=%dn,&i,i);para_adress(i);return 0;可能的运行结果:(运行结果与内存状态有关)实参i的地址:=0X12FF7C;i=10形参x的地址:=0X12FF2C;x=10运行结果表明:形参与实参所占内存是不一样的例7.4 验证实参到形参的单向值传递。源代码:主函数实参:n=5,sm=0子函数:实参送到形参 a=5,s=0子函执行后形参:a=5,s=15函数执行后主函数实参:n=5,sm=0运行结果表明:形参的改变不会引起实参发生改变,即形参到实参是单向值传递的2.函数的值函数的值只能通过return语句返回主调函数return表达式;或者为:
10、return(表达式);函数值的类型和函数定义时规定的函数类型应保持相容如果函数值为整型,在函数定义时可以省去函数类型说明无返回值的函数,必须明确定义为“空类型”,类型说明符为void7.1.3函数声明与函数调用1.函数声明先定义,后使用,这是C语言规则。函数在调用之前必须先对函数返回值数据类型、函数名和函数的参数个数与各个参数的类型进行说明,这就是函数声明(函数原型、函数说明)函数声明以分号结束函数声明可以不包含参数的名字,但必须包含所有参数的类型,用逗号分开2.函数调用函数调用形式:函数名(实际参数表)函数常用的方式:赋值表达式的右值:z=max(x,y);单独的函数语句:printf(%
11、d,a);函数作为实参:printf(%d,max(x,y);函数出现在条件表达式中:if(isprime(x)。例7.5函数作为赋值表达式的右值#include int main()int a,b,s;int sum(int a,int b);/*函数说明*/printf(请输入两个数据a b:n);scanf(%d%d,&a,&b);s=sum(a,b);printf(s=%dn,s);return 0;请输入两个数据a b:4 8 s=12运行结果:例7.6 函数作为单独的函数语句void menu()/*函数的定义*/printf(欢迎使用图书信息管理系统n);printf(1.录入学
12、生成绩n);printf(2.查询学生成绩n);printf(3.修改学生成绩n);printf(4.删除学生成绩n);printf(5.备份学生成绩n);printf(6.退出系统n);#include int main()void menu();/*函数声明*/menu();/*函数调用,函数语句*/return 0;7.2 俄罗斯方块的随机显示全局变量与局部变量、变量的存储类型例7.7俄罗斯方块的随机显示怎么表示俄罗斯方块?可以用一个2维数组来存放一个方块0,0,0,0,1,1,1,1表示:1,0,0,0,1,1,1,0表示:。那存放7种不同的俄罗斯方块需要用一个3维数组:intfk72
13、4=0,0,0,0,1,1,1,1,/*第1种图形*/1,0,0,0,1,1,1,0,/*第2种图形*/0,0,0,1,0,1,1,1,/*第3种图形*/1,1,0,0,1,1,0,0,/*第4种图形*/0,1,1,0,1,1,0,0,/*第5种图形*/0,1,0,0,1,1,1,0,/*第6种图形*/1,1,0,0,0,1,1,0,/*第7种图形*/;显示方块的函数设计:drawfk(int fkn)要显示编号为fkn的方块,只需要用两层循环显示fkfkn中的两行数据即可。void drawfk(int fkn)/*显示第fkn号方块图形*/int i,j;for(i=0;i2;i+)/*分
14、两行显示*/for(j=0;j4;j+)/*每行有4个位置*/if(fkfknij)/*该位置为1*/printf();else printf();printf(n);怎么实现随机显示?随机数与那些函数有关?srand函数函数srand(unsignedzz)通过无符号整数参数zz用于指定随机函数的种子。种子不同,随机函数产生的序列是不同的。rand函数函数rand()用来获得一个界于032767(0 x7FFF)之间的伪随机数,并通过函数值返回。怎么实现随机显示?随机数与那些函数有关?time函数函数time(time_t*timer)是获取或者设置当前的系统时间。当timer为NULL时,
15、表示获取当前系统时间。如果每次都用当前系统时间做随机种子,那么可以保证每次的随机序列是不同的。例7.7俄罗斯方块的随机显示int main()int fkn;/*方块号*/char choice;/*用户的按键*/srand(unsigned)time(NULL);/*初始化随机种子*/fkn=rand()%7;/*产生需要显示的方块序号*/do drawfk(fkn);/*显示方块*/choice=getch();/*等待用户按键*/fkn=rand()%7;/*重新产生需要显示的方块序号*/system(cls);/*在Visual C+表示清除字符界面的函数*/while(choice!
16、=27);/*按键为ESC时退出*/return 0;源程序7.2.1 局部变量和全局变量局部变量局部变量是在函数内部定义的,其作用域仅限于函数内部,所以局部变量也称为内部变量局部变量包含两类:形参与函数内部定义的变量局部变量的作用域:局部变量只在定义的函数内部有用,离开该函数是不能访问的形参变量是属于被调函数的局部变量,它仅仅在被调函数中可以使用允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆在复合语句中也可定义变量,其作用域只在复合语句花括号范围内。如果在复合语句中定义了与复合语句外变量同名的变量,那么在复合语句内只有内部变量有效,外部定义的
17、变量无效。例7.8局部变量的作用域#include int main()int i=1;printf(主函数中变量i地址:%Xn,&i);int i=30;printf(复合语句中变量i地址:%Xn,&i);printf(复合语句中变量i=%dn,i);printf(主函数中变量i=%dn,i);return 0;运行结果:主函数中变量i地址:18FF44复合语句中变量i地址:18FF40复合语句中变量i=30主函数中变量i=1实验表明:复合语句内定义的变量与复合语句外定义的变量站有不同的内存单元,是不同的两个变量!2.全局变量全局变量是在函数外部定义的变量,又称外部变量。它不属于哪一个函数,
18、它属于所在的源程序文件。其作用域是从定义处开始到源程序结束。全局变量说明符extern可以扩展全局变量的作用域如果extern位于函数内部,则全局变量作用域扩展至该函数内extern处到函数尾如果extern位于同一个工程的其他源文件,则全局变量作用域扩展至该源文件extern处到该源文件结尾处例7.9从键盘输入球的半径,用一个函数求出该球的体积与表面积。分析:由于函数只能有一个返回值,要想通过一个函数得到球的面积和体积,可以考虑将表面积放在一个全局变量s中,而函数的返回值为体积,则执行函数之后,体积与表面积都能求出来。源程序extern扩展全局变量的作用域float ballvs(float
19、 r)/*求半径为r的球的体积与表面积*/extern float s;/*因为全局变量 s 是在该函数之后定义的,故这里需要对其进行说明,将作用域扩展到ballvs函数内部*/float v;v=(float)(4*3.14/3*r*r*r);/*求半径为r的球的体积*/s=(float)(4*3.14*r*r);/*求半径为r的球的表面积*/return v;此处不是定义全局变量例7.10 外部变量与局部变量同名时的访问规则。#include int a=3;/*a为全局变量*/int main()int a=8;/*a为局部变量*/printf(%dn,a);return 0;运行结果:
20、8实验结果表明:全局变量与局部变量同名,则在局部变量的作用范围内,全局变量被“屏蔽”。7.2.3 变量的存储类别 在C语言中,程序执行所占内存分为程序区、静态存储区、动态存储区三个部分程序区静态存储区动态存储区程序开始位置代码(只读)数据(读写)程序中的变量:静态存储区的变量在程序开始运行时分配,程序运行结束时收回静态存储区的变量在程序运行期间始终占用固定的存储空间动态存储区的变量是在程序运行期间动态进行分配和回收静态存储区定义的变量缺省值为0,动态存储区定义的变量缺省值是随机的静态区的内存变量的生命周期是整个程序的执行期动态存储区的内存变量的生命周期一般是一个函数的执行期静态区的变量:全局变
21、量用static修饰的变量 动态区的变量:函数形式参数自动变量(未加static声明的局部变量)函数调用时的现场保护与返回地址存储类型auto变量自动型变量,关键字auto可以省略。局部变量如不专门声明为static存储类别,都是动态地分配存储空间的,变量数据存储在动态存储区中。自动局部变量的生命周期是随着函数调用而产生,函数执行结束而自动结束存储类型用static声明局部变量静态局部变量在静态存储区内分配存储单元静态型变量,在整个生命周期只初始化一次。该类型的局部变量的值在函数调用结束后并不释放,以便下次调用时能继续使用如果在定义局部变量时不赋初值,则编译时自动赋初值0静态局部变量的生命周期
22、是随着程序执行开始、程序结束停止存储类型register变量寄存器变量。1.只有局部自动变量和形式参数可以声明为寄存器变量;2.一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;3.局部静态变量不能定义为寄存器变量;4.有些C语言编译器会自动根据变量使用频率选择为register变量;有些C语言编译器即使编程时指定为register变量,执行时根据当时情况仍可能为auto变量存储类型用extern声明全局变量 全局变量本身是分配在静态区,其作用域是定义处到文件的结尾。用extern声明全局变量仅仅是扩展其作用域。用static声明全局变量 用static声明全局变量仅仅是指定该全局
23、变量只在本文件中有效。例7.11静态局部变量实现阶乘。int main()int i;for(i=0;i0)c=c*n;return(c);问题:利用fstatic(intn)来实现阶乘需要条件吗?例7.12 使用寄存器变量求05的阶乘值。intmain()inti;for(i=0;i=5;i+)printf(%d!=%dn,i,fac(i);return0;intfac(intn)/*求阶乘*/registerinti,f=1;for(i=1;i1)hanoi(A,C,B,n-1);/*第一步*/printf(%c-%cn,A,C);/*第二步*/hanoi(B,A,C,n-1);/*第三步
24、*/elseif(n=1)/*问题规模降低到1时*/printf(%c-%cn,A,C);源程序函数的嵌套调用函数虽然不能嵌套定义,但可以嵌套调用。例如,可以在main函数中调用sectionPrime函数,在sectionPrime函数中又调用printf函数。递归函数Hanoi(A,B,C,n)函数在实现的过程中,调用了自身,像着这样的直接或间接调用自身的函数,称为:递归函数。递归函数是嵌套调用的一种特例,即调用的是本身。间接递归递归函数的特征1.必须有一个递归终止条件。2.递归时问题规模应该逐步降低。3.从逻辑上来讲,上述两点一定分属于一个选择结构中的两个分支,否则不能逻辑终止。例7.1
25、4 求斐波那契(Fibonacci)数列的第10项。Fibonacci数列的数学公式如下:代码:intfib(intn)if(n2)returnfib(n-1)+fib(n-2);/*递归,问题规模降低*/elseif(n=1|n=2)/*递归终止条件*/return1;elsereturn-1;源程序例7-15编程求10!的值。什么是阶乘?longfact(intn)if(n=1)/*终止条件*/return1;elsereturn(n*fact(n-1);源程序递归与循环递归程序是通过多次调用自身函数来实现循环递归过程中,调用自身函数需要用到系统堆栈递归的效率比循环结构低递归的层次不能太多
26、7.4明文与密文数组名作函数参数例7.16 将一密码明文加密,并将该密文在屏幕上解密输出。用某种方法重新组合数据以隐藏明文的内容,这个过程称为加密把密文转变为明文的过程称为解密加密算法有很复杂的算法,也有很简单的加密算法,C语言中有一个位运算符可以实现很简单的加密算法。假如a是明文字符,则ab就成了密文,密文(ab)b=a,故密文再一次与b异或就还原成明文了。加密解密算法:voidcoding(charstr,chark)/*str是明文或密文的字符串,k是密钥*/inti,num;num=strlen(str);/*num是字符串的长度*/for(i=0;inum;i+)stri=strik
27、;/*异或运算*/源程序1.数组元素作函数实参数组元素就是下标变量,它与普通变量并无区别。如果它作为函数实参与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。例7.17 数组中存有一个班学生的成绩,请编程打印该班的等级成绩。A:90分及以上,B:80分及以上,C:60分及以上,D:60以下。源程序2.数组名作为函数参数在加密解密算法coding函数中,形参参数采用的是数组名,在算法中对形参数组的元素进行了加密异或运算,结果实参数组的元素变成了密文。形参实参:”单向值传送”失效了吗?为什么会有这种现象?2.数组名作为函数参数数组名:是数组的首地址,是
28、一个常量数组名做函数参数,函数调用调用时:实参是是数组的首地址,是一个常量,传送到形参,形参也就是数组的首地址。形参实参:”单向值传送”依然有效为什么不传送整个数组的元素到形参?效率很低空间消耗大例7-18数组a中存放了一个学生5门课程的成绩,设计函数求学生的平均成绩。为了表示数组的长度,在设计数组名做函数参数时,可以设计一个参数n表示数组的大小。floataver(floata,intn)inti;floatav,s=0;for(i=0;in;i+)s=s+ai;av=s/n;returnav;源程序数组名作为函数参数时的参数传送用数组名作为函数参数时的注意点:形参数组和实参数组的类型必须一
29、致,否则将引起错误。实参虽然能把数组首地址传给形参,但不能告知数组的长度。多维数组也可以作为函数的参数。在函数定义时对形参数组可以指定每一维的长度,仅仅可省去第一维的长度。7.5内部函数和外部函数随着软件规模的不断增长,软件的开发通常需要团队的通力合作才能完成,软件所包含的源文件数也变得越来越多。一个源文件中的某些函数可以被同一工程中的所有源文件共享;而某些函数仅仅在其源文件内部使用,这样可以避免函数名的冲突,也保证了该函数的私用性。函数能被其他源文件的函数调用,则为外部函数(缺省值)。函数不能被其他源文件的函数调用,则为内部函数,函数前要用static来说明。例7.19 file1.c,fi
30、le2.c,file3.c,file4.c四个源代码文件协同完成从键盘输入一个字符串,程序实现将字符串中指定的字符删去,然后输出修改后的字符串。file1.c文件定义一个主函数main,调用其他文件中实现的函数来完成本程序的功能。file2.c文件定义enter_string函数,采用gets函数从键盘接受一个字符串。file3.c文件定义delete_string函数,遍历字符串,把指定的字符删除,并把后面的字符向前移动一位。file4.c文件定义print_string函数,把字符串输出到显示器。工程结构:文件file2.c、file3.c、file4.c中的函数都需要被main函数调用,它们都是外部函数。源程序谢谢第7章结束