C语言桂林理工大学7第七章 函数及其应用.docx
- 文档编号:10835500
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:30
- 大小:163.57KB
C语言桂林理工大学7第七章 函数及其应用.docx
《C语言桂林理工大学7第七章 函数及其应用.docx》由会员分享,可在线阅读,更多相关《C语言桂林理工大学7第七章 函数及其应用.docx(30页珍藏版)》请在冰豆网上搜索。
C语言桂林理工大学7第七章函数及其应用
学时安排:
5
授课题目:
第七章函数及其应用
目标要求:
掌握函数的定义与调用,理解函数间的信息传递方式,掌握数组做为函数参数时数据传递方式,了解递归调用。
掌握变量的存储类型及作用域。
授课类型:
理论课授课场地:
教室
教学方式:
讲授、演示、提问、讨论教辅设备:
投影仪
授课内容:
第七章函数及其应用
模块化程序设计介绍
7、1函数的定义
7.1.1C语言的函数:
C语言的函数是子程序的总称,包括函数和过程。
(有返回值、无返回值,教材中称为:
有返回值函数,无返回值函数)。
C语言函数可以分为库函数、用户自定义函数。
库函数由系统提供,程序员只需要使用(调用),用户自定义函数需要程序员自己编制。
C语言的程序由函数组成,函数是C语言程序的基本单位。
前面章节介绍的所有程序都是由一个主函数main组成的。
程序的所有操作都在主函数中完成。
事实上,C语言程序可以包含一个main函数,也可以包含一个main函数和若干个其它函数。
C语言程序的结构如图所示。
在每个程序中,主函数main是必须的,它是所有程序的执行起点,main函数只调用其它函数,不能为其它函数调用。
如果不考虑函数的功能和逻辑,其它函数没有主从关系,可以相互调用。
所有函数都可以调用库函数。
程序的总体功能通过函数的调用来实现。
e函数
d函数
c函数
a函数
b函数
函数应当先定义,后调用。
7.1.2.函数定义的一般形式:
[函数类型]函数名([函数参数类型1函数参数名1][,…,函数参数类型2,函数参数名2])
{
[声明部分]
[执行部分]
}
函数头
函数体
说明:
一个函数(定义)由函数头(函数首部)和函数体两部分组成
1、函数头(首部):
说明了函数类型、函数名称及参数。
(1)函数类型:
函数返回值的数据类型,可以是基本数据类型也可以是构造类型。
如果省略默认为int,如果不返回值,定义为void类型。
(2)函数名:
给函数取的名字,以后用这个名字调用。
函数名由用户命名,命名规则同标识符。
(3)函数名后面是参数表,无参函数没有参数传递,但“()”号不能省略,这是格式的规定。
参数表说明参数的类型和形式参数的名称,各个形式参数用“,”分隔。
2、函数体:
函数首部下用一对{}括起来的部分。
如果函数体内有多个{},最外层是函数体的范围。
函数体一般包括声明部分、执行部分两部分。
(1)声明部分:
在这部分定义本函数所使用的变量和进行有关声明(如函数声明)。
(2)执行部分:
程序段,由若干条语句组成命令序列(可以在其中调用其它函数)。
注意:
函数不能单独运行,函数可以被主函数或其它函数调用,也可以调用其它函数,但是不能调用主函数。
例:
输入三个整数,求三个整数中的最大值,打印。
使用函数
externintmax(int,int,int);
main()
{
intn1,n2,n3,nmax;
scanf(“%d%d%d”,&n1,&n2,&n3);
nmax=max(n1,n2,n3);
printf(“max=%d\n“,nmax);
}
intmax(intx,inty,intz)
{
intm;
if(x>y)
m=x;
else
m=y;
if(z>m)m=z;
returnm;
}
不使用函数(除main外)
main()
{
intn1,n2,n3,nmax;
scanf(“%d%d%d”,&n1,&n2,&n3);
if(n1>n2)
nmax=n1;
else
nmax=n2;
if(n3>nmax)
nmax=n3;
printf(“max=%d\n”nmax);
}
像调用库函数一样调用
函数定义
说明:
(1)比较两个程序,使用函数好象程序更长了,但是请思考,如果程序中要调用100次求三个数最大值又会是什么情况呢?
(2)解释max函数的定义的几个部分。
简要解释max函数的调用。
7、2函数的调用
7.2.1函数调用的一般方法
函数名([实参表列])[;]
说明:
(1)无参函数调用没有参数,但是“()”不能省略,有参函数若包含多个参数,各参数用“,”分隔,实参参数个数与形参参数个数相同,类型一致或赋值兼容。
(2)函数调用可以出现的位置
●以单独语句形式调用(注意后面要加一个分号,构成语句)。
以语句形式调用的函数可以有返回值,也可以没有返回值。
例如:
printf(“max=%d”,nmax);
swap(x,y);
puts(s);
●在表达式中调用(后面没有分号)。
在表达式中的函数调用必须有返回值。
例如:
if(strcmp(s1,s2)>0)……//函数调用strcmp()在关系表达式中。
nmax=max(n1,n2,n3);//函数调用max()在赋值表达式中,“;”是赋值表达式作为语句时加的,不是max函数调用的。
fun1(fun2());//函数调用fun2()在函数调用表达式fun1()中。
函数调用fun2()的返回值作为fun1的参数。
7.2.2函数调用时数据的传递(函数之间的通讯)
函数是相对独立的,但是不是孤立的,它们通过调用时1)参数传递和2)函数的返回值3)全局变量(后面介绍)来相互联系。
已经介绍了函数参数的传递和返回值。
现在以数组应用为例介绍参数传递中的传址方式的使用。
例:
求学生平均成绩。
P110.(注意参数传递的是数组的地址)
思考:
如果在函数ave中加入程序段:
for(i=0;i b[i]=sqrt(b[i])*10; 那么实参数组a中的数据是否改变? 7.2.3函数的声明 函数定义的位置可以1)在调用它的函数之前,也可以2)在调用它的函数之后,甚至位于3)其它的源程序模块中。 ●函数定义位置在前,函数调用在后,不必声明,编译程序产生正确的调用格式。 ●函数定义在调用它的函数之后或者函数在其它源程序模块中,且函数类型不是整型,这时,为了使编译程序产生正确的调用格式,可以在函数使用前对函数进行声明。 这样不管函数在什么位置,编译程序都能产生正确的调用格式。 函数声明的格式: 函数类型函数名([参数类型][,…,[参数类型]]); C语言的库函数就是位于其它模块的函数,为了正确调用,C编译系统提供了相应的.h文件。 .h文件内许多都是函数声明,当源程序要使用库函数时,就应当包含相应的头文件。 7、3函数的信息传递方式 7.3.1形式参数与实际参数 1、形式参数(形参): 函数定义时设定的参数。 2、实际参数(实参): 调用函数时所使用的实际的参数。 例中,主函数中调用max函数的语句是: nmax=max(n1,n2,n3);其中n1,n2,n3就是实参,它们的类型都是整型。 3、参数的传递 在调用函数时,主调函数和被调函数之间有数据的传递-实参传递给形参。 具体的传递方式有两种: (1)值传递方式(传值): 将实参单向传递给形参的一种方式。 (2)地址传递方式(传值): 将实参地址单向传递给形参的一种方式。 注意: (1)单向传递: 不管“传值”、还是“传址”,C语言都是单向传递数据的,一定是实参传递给形参,反过来不行。 也就是说C语言中函数参数传递的两种方式本质相同-“单向传递”。 (2)“传值”、“传址”只是传递的数据类型不同(传值-一般的数值,传址-地址)。 传址实际是传值方式的一个特例,本质还是传值,只是此时传递的是一个地址数据值。 (3)系统分配给实参、形参的内存单元是不同的,也就是说即使在函数中修改了形参的值,也不会影响实参的值。 ●对于传值,即使函数中修改了形参的值,也不会影响实参的值。 ●对于传址,即使函数中修改了形参的值,也不会影响实参的值。 但是,注意: 不会影响实参的值,不等于不影响实参指向的数据。 传址与传值一样不能通过参数返回数据,但因为传递的是地址,那么就可能通过实参参数所指向的空间间接返回数值。 数据单向传递 ax 主调函数被调函数 不同的存储空间 传值(传递一般的数值) a,x是不同的空间 数据单向传递 但是a 是地址 指向: 一块 内存 空间 ax 主调函数被调函数 不同的存储空间 传址(传递地址数值) a,x也是不同的空间 (4)两种参数传递方式中,实参可以是变量、常量、表达式;形参一般是变量,要求两者类型相同或赋值兼容。 7.3.2函数的返回值 C语言可以从函数(被调用函数)返回值给调用函数(这与数学函数相当类似)。 在函数内是通过return语句返回值的。 使用return语句能够返回一个值或不返回值(此时函数类型是void)。 ` Return语句的格式: Return[表达式];或return(表达式); 说明: (1)函数的类型就是返回值的类型,return语句中表达式的类型应该与函数类型一致。 如果不一致,以函数类型为准(赋值转化)。 (2)函数类型省略,默认为int。 (3)如果函数没有返回值,函数类型应当说明为void(无类型)。 7、4函数与数组 数组作为参数时的情况: –数组元素作实参,与单个变量一样 –数组名作函数参数,形、实参数都应是数组名或指针 –实参数组与形参数组的大小可以不同,但实参>形参; 7.4.1数组元素作实参 7.4.2一维数组名作实参 7、5递归函数递归调用 函数允许嵌套调用和递归调用。 递归调用是嵌套调用的特例。 7.5.1函数的嵌套调用 函数嵌套调用: 函数调用中又存在调用。 如函数1调用函数2,函数又调用函数3。 函数之间没有从属关系,一个函数可以被其它函数调用,同时该函数也可以调用其它函数。 例验证歌德巴赫猜想。 按照歌德巴赫猜想,任何一个偶数均可以表示为两个质数之和。 如16=13+338=7+31…… 程序一 #include main() { intprimeok(unsignedintn); intproveok(unsignedintn); unsignedn,m1,m2; for(n=6;n<=20;n+=2) { m1=proveok(n); m2=n-m1; printf("%u=%u+%u\n",n,m1,m2); } } intproveok(unsignedintn) {unsignedinti; intn1,n2; for(i=3;i<=n;i++) {n1=i*primeok(i); n2=(n-i)*primeok(n-i); if(n==n1+n2)break; } return(i); } intprimeok(unsignedintn) {inti,m; m=sqrt(n); for(i=2;i<=m;i++) if(n%i==0)break; if(i>=m+1) return (1); else return(0); } 程序二 #include main() { intprimeok(unsignedintn); intproveok(unsignedintn); unsignedn,m1,m2; for(n=6;n<=20;n+=2) { m1=proveok(n); m2=n-m1; printf("%u=%u+%u\n",n,m1,m2); } } intproveok(unsignedintn) {unsignedinti; intn1,n2; for(i=3;i<=n;i++) {n1=i; n2=n-i; if((n==n1+n2)&&(primeok(n1)==1)&&(primeok(n2)==1))break; } return(i); } intprimeok(unsignedintn) {inti,m; m=sqrt(n); for(i=2;i<=m;i++) if(n%i==0)break; if(i>=m+1) return (1); else return(0); } 7.5.2函数的递归调用 1、函数的递归调用: 是指函数直接调用或间接调用自己,或调用一个函数的过程中出现直接或间接调用该函数自身。 前者称为直接递归调用,后者称为间接递归调用。 例如: ->f1()->f1()直接递归调用;->f1()->f2()->f1()间接递归调用。 2、使用递归调用解决问题的方法: (有限递归) (1)原有的问题能够分解为一个新问题,而新问题又用到了原有的解法,这就出现了递归。 (2)按照这个原则分解下去,每次出现的新问题是原有问题的简化的子问题 (3)最终分解出来的新问题是一个已知解的问题。 3、递归调用过程(两个阶段) (1)递推阶段: 将原问题不断地分解为新的子问题,逐渐从未知的向已知的方向推测,最终达到已知的条件,即递归结束条件,这时递推阶段结束。 (2)回归阶段: 从已知条件出发,按照“递推”的逆过程,逐一求值回归,最终到达“递推”的开始处,结束回归阶段,完成递归调用。 例: 用递归法求n! (P.114) 解: n! =n*(n-1)*(n-2)*……*1=n(n-1)! 。 递归公式: 10,1 n! = n*(n-1)! 其它 main() { floatfac(intn); inti; for(i=1;i<=20;i++) printf("%d! =%25.0f\n",i,fac(i)); } floatfac(intn) { floatf; if(n<0)printf("n<0,dataerror! "); elseif(n==0||n==1)f=1; elsef=fac(n-1)*n; return(f); } 7.6变量的存储类型及作用域 变量从空间上分为局部变量、全局变量。 从变量存在的时间的长短(即变量生存期)来划分,变量还可以分为: 动态存储变量、静态存储变量。 变量的存储方式决定了变量的生存期。 C语言变量的存储方式可以分为: 动态存储方式、静态存储方式。 自动(局部变量)(auto) 动态存储方式 寄存器(局部变量)(register) 存储方式 静态(局部变量)(static) 静态存储方式 静态全局变量(全局变量全部是静态的,不必用static修饰) 动态存储方式 动态存储方式: 在程序运行期间根据需要为相关的变量动态分配存储空间的方式。 C语言中,变量的动态存储方式主要有自动型存储方式和寄存器型存储方式。 1、自动型存储方式(auto) auto型存储方式是C语言默认的局部变量的存储方式,也是局部变量最常使用的存储方式。 (1)自动变量属于局部变量的范畴,作用域限于定义它的函数或复合语句内。 (2)自动变量所在的函数或复合语句执行时,系统动态为相应的自动变量分配存储单元,当自动变量所在的函数或复合语句执行结束后,自动变量失效,它所在的存储单元被系统释放,所以原来的自动变量的值不能保留下来。 若对同一函数再次调用时,系统会对相应的自动变量重新分配存储单元。 [auto]类型说明变量名; [register]类型说明变量名; 自动(局部)变量的定义格式: 其中: auto为自动存储类别关键词,可以省略,缺省时系统默认auto。 例如: 前面各章中的函数中的局部变量,尽管没有明确定义为auto型,但它们都属于auto型变量。 在函数中定义变量,下面两种写法是等效的。 intx,y,z;或autointx,y,z;它们都定义了3个整型auto型变量x,y,z。 2、寄存器型存储方式(register) register型存储方式是C语言使用较少的一种局部变量的存储方式。 该方式将局部变量存储在CPU的寄存器中,寄存器比内存操作要快很多,所以可以将一些需要反复操作的局部变量存放在寄存器中。 寄存器(局部变量)的定义格式: 其中: register为寄存器存储类别关键词,不能省略。 值得注意的是: CPU的寄存器数量有限,如果定义了过多的register变量,系统会自动将其中的部分改为auto型变量。 静态存储方式 静态存储方式: 在程序编译时就给相关的变量分配固定的存储空间(在程序运行的整个期间内都不变)的存储方式。 C语言中,使用静态存储方式的主要有静态存储的局部变量和全局变量。 [static]类型说明变量名[=初始化值]; 1、静态存储的局部变量 静态局部变量的定义格式: 其中: static是静态存储方式关键词,不能省略。 例如: 在函数内定义: staticinta=10,b; 说明: (1)静态局部变量的存储空间是在程序编译时由系统分配的,且在程序运行的整个期间都固定不变。 该类变量在其函数调用结束后仍然可以保留变量值。 下次调用该函数,静态局部变量中仍保留上次调用结束时的值。 (2)静态局部变量的初值是在程序编译时一次性赋予的,在程序运行期间不再赋初值,以后若改变了值,保留最后一次改变后的值,直到程序运行结束。 2、全局变量全部是静态存储的。 C语言中,全局变量的存储都是采用静态存储方式,即在编译时就为相应全局变量分配了固定的存储单元,且在程序执行的全过程始终保持不变。 全局变量赋初值也是在便宜时完成的。 因为全局变量全部是静态存储,所以没有必要为说明全局变量是静态存储而使用关键词static。 如果使用了static说明全局变量,不是说“此全局变量要用静态方式存储”(我们知道全局变量天生是静态存储的),那是有另外的含义-令人困惑的全局变量的static说明。 3、全局变量的extern声明及令人困惑的全局变量的static定义。 全局变量的static定义,不是说明“此全局变量要用静态方式存储”(全局变量天生全部是静态存储),而是说,这个全局变量只在本源程序模块有效(文件作用域)。 如果没有static说明的全局变量就是整个源程序范围有效(真正意义上的全局)。 也就是说,变量的作用域有: 分程序(复合语句)作用域,函数作用域,文件(模块)作用域,整个程序作用域。 在引用全局变量时如果使用“extern”声明全局变量,可以扩大全局变量的作用域。 例如,扩大到整个源文件(模块),对于多源文件(模块)可以扩大到其它源文件(模块)。 变量从数据类型的角度,可以分为整型、实型、字符型等。 变量的作用域: 变量的有效范围或者变量的可见性。 变量定义的位置决定了变量的作用域。 分程序作用域(复合语句作用域) 局部变量 函数作用域 变量的作用域 文件作用域(模块作用域) 全局变量 程序作用域 变量从作用域(变量的有效范围,可见性)的角度可以分为: 局部变量,全局变量。 1局部变量 1、局部变量: 是指在一定范围内有效的变量。 C语言中,在以下各位置定义的变量均属于局部变量。 (1)在函数体内定义的变量,在本函数范围内有效,作用域局限于函数体内。 (2)在复合语句内定义的变量,在本复合语句范围内有效,作用域局限于复合语句内。 (3)有参函数的形式参数也是局部变量,只在其所在的函数范围内有效。 例如: doublefun1(intx,inty)/*x,y,m,n局部变量,在fun1函数内有效(作用域fun1函数)*/ { intm,n; ...... } intfun2(charch)/*ch,a,b局部变量,在fun2函数内有效(作用域fun2函数)*/ { inta,b; ...... } main()/*a,b局部变量,在main函数内有效(作用域main函数)*/ { inta,b; ...... { intx,y;/*x,y局部变量,在复合语句中有效(作用域复合语句)*/ ...... } } 同名,不同作用域,是不同的变量 说明: (1)不同函数中和不同的复合语句中可以定义(使用)同名变量。 因为它们作用域不同,程序运行时在内存中占据不同的存储单元,各自代表不同的对象,所以它们互不干预。 即: 同名,不同作用域的变量是不同的变量。 (2)局部变量所在的函数被调用或执行时,系统临时给相应的局部变量分配存储单元,一旦函数执行结束,则系统立即释放这些存储单元。 所以在各个函数中的局部变量起作用的时刻是不同的。 2、全局变量 全局变量: 在函数之外定义的变量。 (所有函数前,各个函数之间,所有函数后) 全局变量作用域: 从定义全局变量的位置起到本源程序结束为止。 ●在引用全局变量时如果使用“extern”声明全局变量,可以扩大全局变量的作用域。 例如,扩大到整个源文件(模块),对于多源文件(模块)可以扩大到其它源文件(模块)。 ●在定义全局变量时如果使用修饰关键词static,表示此全局变量作用域仅限于本源文件(模块)。 例如: x,y,z ch1,ch2 t,p 全部是全局变量 intx,y,z; floatf1(floata,floatb) { ...... } charch1,ch2; intf2(intm) { ...... } doublet,p; main() { ...... } x,y,z的作用域 ch1,ch2的作用域 t,p的作用域 externdoublet,p; intx,y,z; floatf1(floata,floatb) { t=a+b; ...... } charch1,ch2; intf2(intm) { externdoublet,p; ...... } doublet,p; main() { ...... } 扩大了全局变量t,p的作用域 为整个文件(模块) 扩大了全局变量t,p的作用域 file2.c externintx; func(inta) { ...... x=x+a; } 作用域从file1扩大到file2 file1.c intx; main() { ...... } 作用域扩大到file2.c file3.c intx; func2() { ...... } file2.c externintx; func1(inta) { ...... x=x+a; } file1.c staticintx; main() { ...... } 说明:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C语言桂林理工大学7第七章 函数及其应用 语言 桂林 理工大学 第七 函数 及其 应用