1、单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,2021/10/3,#,第九章 函数,第1页,本章学习内容,本章首先总括说明了,C,语言函数与程序结构。然后介绍了函数定义、函数参数、函数返回值、函数调用、函数嵌套调用和函数递归调用。还介绍了数组作为函数参数。介绍了局部变量与全局变量以及变量存放类别。经过一个详细多源文件程序例子说明了工程文件创建和生成可执行文件方法。最终介绍了编译预处理相关知识。,第2页,本章学习目标,掌握函数定义和调用方法,使学生深入掌握结构化程序设计方法。了解形式参数、实际参数、传值和传地址基本概念。能设计含有自定义函数程序,包含函数嵌套
2、调用以及函数递归调用程序。掌握变量作用域和变量生存期,掌握编译预处理相关知识。,第3页,本章学习重点和难点,重点:,1函数定义。,2,主调函数与被调函数之间,参数传递,。,难点:,函数递归调用。,第4页,第九章 函数,9.1,概述,9.2,函数定义普通形式,9.3,函数参数和函数值,9.4,函数调用,9.5,函数嵌套调用,9.6,函数递归调用,9.7 数组作为函数参数,9.8 局部变量和全局变量,9.9 变量存放类别,9.10 工程文件,9.11,编译预处理,第5页,9.1,概述,一个C程序可由一个主函数和若干个函数组成。由主函数调用其它函数,其它函数也能够相互调用。同一个函数能够被一个或多个
3、函数调用任意屡次。,第6页,9.1,概述,在程序设计中,经常将一些惯用功效模块编写成函数,放在函数库中供选取。从而降低程序段重复编写。,第7页,9.1,概述,例1 函数printstar()功效为打印一行*号,函数print_message()功效为打印Happy New Year!,按以下格式在屏幕上输出:,*,Happy New Year!,*,第8页,9.1,概述,说明:,(1),一个源程序文件由一个或多个函数组成,。一个源程序文件是一个编译单位,即以源程序为单位进行编译,而不是以函数为单位进行编译。,(2),一个C程序由一个或多个源程序文件组成,。对于较大程序,普通不希望全部放在一个文
4、件中,而将函数和其它内容分别放在若干个源文件中,再由若干个源文件组成一个C程序。这么能够分别编写、分别编译,提升工作效率。一个源文件能够为多个C程序公用。,第9页,9.1,概述,(3),C程序执行从main函数开始,条用其它函数后流程返回到main函数,在main函数中结束整个程序运行。main函数是系统定义,。,(4),全部函数都是平行,即在定义函数时是相互独立,一个函数名不隶属另一函数,即函数不能嵌套定义。函数间能够相互调用,不过不能调用main函数,。,第10页,9.1,概述,(5),从用户使用角度看,函数可分为两类,:,标准函数,,即库函数。,这是由系统定义,用户能够直接使用,但在使用
5、前要将对应头文件包含到程序中去。,用户自己定义函数,。,用以处理用户专门需要。,第11页,9.1,概述,(6),从函数形式看,函数可分为两类,:,无参函数,。,例1中printstar和print_message函数就是无参函数。,有参函数,。,在调用函数时,在主调函数和被调函数之间有数据传递。也就是说,主调函数能够将数据传给被调函数使用,被调用函数中数据也能够带回来供主调函数使用。,第12页,9.2,函数定义普通形式,1,无参函数定义形式,类型标识符,函数名(),申明部分,语句,例,1,中,printstar,和,print_message,函数就是无参函数。,第13页,9.2,函数定义普通
6、形式,2,有参函数定义普通形式,类型标识符,函数名(形式参数列表),申明部分,语句,第14页,9.2,函数定义普通形式,3,能够有“空函数”,形式:,类型标识符,函数名(),比如:,dummy(),第15页,9.2,函数定义普通形式,例1 求出2个任意整数中最大数,并输出。,/*,求两个任意整数中最大值,*/,/*,当代形式参数说明方式,*/,main(),int a,b,c;,printf(,请输入,a,b:);,scanf(%d,%d,c=max(a,b);,printf(Max is:%dn,c);,int max(int x,int y)/*,定义有参函数,max*/,int z;/*
7、函数中说明部分,*/,z=xy?x:y;,return(z);,第16页,9.2,函数定义普通形式,例,2,编写学生成绩管理系统菜单程序。,第17页,9.2,函数定义普通形式,4,对形参申明传统方式,/*,求两个任意整数中最大值,*/,/*,传统形式参数说明方式,*/,main(),int a,b,c;,printf(,请输入,a,b:);,scanf(%d,%d,c=max(a,b);,printf(Max is:%dn,c);,int max(x,y)/*指定参数x,y*/,int x,y;/*对参数进行类型说明*/,int z;,z=xy?x:y;,return(z);,第18页,9.
8、2,函数定义普通形式,普通把这种方法称为传统对形式参数申明方式,而把前面介绍方法称为当代方式。Turbo C和当前使用多数C版本对这两种方法都允许使用,两种方法等价,ANSI新标准推荐前一个方法即当代方式。,第19页,9.3,函数参数和函数值,9.3.1,形式参数和实际参数,9.3.2,函数返回值,第20页,9.3.1,形式参数和实际参数,形式参数在定义函数时函数名后面圆括弧中变量名称为形式参数,简称为形参。,实际参数在主调函数中调用一个函数时,函数名后面圆括弧中参数称为实际参数,简称为实参。,第21页,9.3.1,形式参数和实际参数,例1 调用函数时数据传递。,第22页,9.3.1,形式参数
9、和实际参数,关于形参和实参说明,:,(1),在定义函数中指定形参,在未出现函数调用时,它们并不占内存中存放单元。只有在发生函数调用时,函数中形参才由系统分配内存单元。在调用结束后,形参所占内存单元也被释放。,第23页,9.3.1,形式参数和实际参数,(2),实参能够是常量、变量或表示式。但要求它们有确定值,在调用时将实参值赋给形参(假如是数组名,则传递是数组首地址而不是数组值)。,比如:,max(3,a+b);,第24页,9.3.1,形式参数和实际参数,(3),在被定义函数中,必须指定形参类型。,第25页,9.3.1,形式参数和实际参数,(,4,),实际参数与实际参数类型应性同或赋值兼容。,假
10、如实际参数为整型而形式参数为实型,或者相反,则按照前面介绍不一样类型数据赋值规则进行转换。比如,实际参数值a为3.5,而形式参数x为整型,则将实数3.5转换成整型数3,然后送到形式参数b。但此时应将max函数放在main函数前面或在main函数中对被调用函数max作原型申明,不然会犯错。,第26页,9.3.1,形式参数和实际参数,(5),C语言要求,实参变量对形参变量数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。,第27页,9.3.1,形式参数和实际参数,例2 交换两个变量值。注意:C语言中,因为参数传递是单向,所以这是一个错误程序。,第28页,9.3.1,形式
11、参数和实际参数,例3 分析以下程序。,main(),int x=2,y=3,z=0;,printf(*x=%d,y=%d,z=%dn,x,y,z);,try(x,y,z);/*,函数调用,x,y,z,为实参,*/,printf(*x=%d,y=%d,z=%dn,x,y,z);,try(int x,int y,int z),printf(*x=%d,y=%d,z=%dn,x,y,z);,z=x+y;,x=x*x;,y=y*y;,printf(*x=%d,y=%d,z=%dn,x,y,z);,第29页,9.3.2,函数返回值,说明,:,(1),函数返回值是经过函数中return语句取得。retur
12、n语句将被调用函数中一个确定值带回主调函数中去。,第30页,9.3.2,函数返回值,例4 已知x,求y值。,2x2-x x0,y=,2x2 x0,第31页,9.3.2,函数返回值,(2),函数值类型。,既然函数有返回值,这个值当然应属于某一个确定类型,应该在定义函数时指定函数值类型。比如:,int max(int x,int y),C语言要求,,凡不加类型说明函数,一律自动按整型处理。,第32页,9.3.2,函数返回值,(3),假如函数值类型和return语句中表示式值不一致,则以函数类型为准。对数值型数据,能够自动进行类型转换。即函数类型决定返回值类型。,第33页,9.3.2,函数返回值,(
13、4),假如被调用函数中没有return语句,并不带回一个切顶、用户所希望得到函数值,但实际上,函数并不是不带回值,而只是不带回有用值,带回是一个不确定值。,(5),为了明确表示“不带回值”,能够用“void”定义无类型(或称为空类型)。,第34页,9.3.2,函数返回值,例6 将前面打印程序中printstar()和print_message()函数定义成为无类型函数。,第35页,9.4,函数调用,9.4.1,函数调用普通形式,9.4.2,函数调用方式,9.4.3,对被调用函数申明和函数原型,第36页,9.4.1,函数调用普通形式,函数调用普通形式,:,函数名(,);,第37页,9.4.1,函
14、数调用普通形式,说明,:,(,1,)假如是调用无参函数,则,能够省略,不过括号不能省。,(,2,)假如实际参数列表中包含多个实际参数,则个参数之间用逗号隔开。,(,3,)实际参数与形式参数个数应相等,类型应一致。实际参数与形式参数按次序对应,一一传递。,第38页,9.4.1,函数调用普通形式,(,4,)假如实际参数列表中包含多个参数,对实际参数求值次序接顶于编译系统。有系统按自左至右,有系统则按自右至左次序。比如,Turbo C,和,MS C,是按自右至左次序求值。,第39页,9.4.2,函数调用方式,1,函数语句,main(),printstar();/*,调用,printstar,函数,*
15、/,print_message();/*,调用,print_message,函数,*/,printstar();/*,调用,printstar,函数,*/,第40页,9.4.2,函数调用方式,2,函数表示式,函数出现在一个表示式中,这种表示式称为函数表示式。这时要求函数带回一个确定函数值参加表示式运算。比如:,c=2*max(a,b);,第41页,9.4.2,函数调用方式,3,函数参数,函数调用作为一个函数参数。比如:,printf(“%d”,max(a,b);,第42页,9.4.3,对被调用函数申明和函数原型,被调用函数应具备条件:,(1)被调用函数必须似乎已经存在函数,能够是库函数或用户自
16、定义函数。,(2)假如使用库函数,必须在文件开头使用#include命令将调用相关库函数全部需要信息包含到本文件中来。,第43页,9.4.3,对被调用函数申明和函数原型,(3)假如使用用户自定义函数,而且该函数与调用它函数在用一个文件中,普通还应在主调用函数中对被调用函数作申明。,申明,向编译系统申明将要调用函数,并将相关信息通知编译系统。,第44页,9.4.3 对被调用函数申明和函数原型,以上函数申明称为,函数原型,。,使用函数原型作用主要是利用它在程序编译阶段对函数正当性做全方面检验。,第45页,9.4.3 对被调用函数申明和函数原型,函数原型普通形式,:,形式1:,函数类型 函数名(,)
17、形式2:,函数类型 函数名(,);,第46页,9.4.3 对被调用函数申明和函数原型,说明,:,(1)假如在函数调用之前没有对函数作申明,则编译系统把第一次碰到函数形式(函数定义或者函数调用)作为函数申明,并将函数类型默认为int型。,(2)假如被调用函数定义出现在主调用函数之前,能够无须加以申明。因为编译系统已经先知道了定义函数类型,会依据函数首部提供信息对函数调用作正确性检验。,第47页,9.4.3 对被调用函数申明和函数原型,(3)假如已在全部函数定义之前,在函数外部已经做了函数申明,则在各个函数中无须对所调用函数再作申明。,第48页,9.4.3 对被调用函数申明和函数原型,例2 求
18、两个任意整数中最大值。,第49页,9.4.3 对被调用函数申明和函数原型,例3 求两个任意整数之和,在函数外部申明。,第50页,9.5,函数嵌套调用,函数嵌套调用一个函数调用另一个函数,这个函数又调用另外一个函数,这么调用称为函数嵌套调用。,第51页,9.6,函数递归调用,9.6.1,递归概念,9.6.2,递归程序设计应用举例,第52页,9.6.1,递归概念,递归通俗地讲,用本身结构来描述本身,就称为递归。,递归调用在调用一个函数过程中,又出现直接地或间接地调用该函数本身,这种函数调用称为递归调用。,第53页,9.6.2,递归程序设计应用举例,例1 用递归求n!。,1 n=0,n!=,n*(n
19、1)!n 0,第54页,9.6.2,递归程序设计应用举例,例2 用递归调用方法计算x,n,。,1 n=0,x,n,=,x*x,n-1,n 0,第55页,9.6.2,递归程序设计应用举例,例3,用递归求,2,个正整数最大条约数。,辗转相除法:,n m Mod n=0,gcd(m,n)=,gcd(n,m Mod n)m Mod n0,第56页,9.7 数组作为函数参数,1,数组元素作函数参数,数组元素作函数参数传值,与用变量作实际参数一样,数组元素作函数实际参数是单向传递,属于值传送。,第57页,9.7 数组作为函数参数,例,1,求,10,个数中最大值。,b,最大值,第58页,9.7 数组作为函
20、数参数,2,数组名作函数参数,数组名作函数参数传地址,第59页,9.7 数组作为函数参数,说明:,(1)假如形式参数是数组形式,则实际参数必须是实际数组名,假如实际参数是数组名,则形式参数能够是一样维数数组名或指针。,(2)在主调用函数和被调用函数中分别定义数组。,(3)实参数组和形参数组必须类型一致,形参数组能够不指明长度。,第60页,9.7 数组作为函数参数,(4)在C中,数组名除了作变量标识符外,数组明换代表了该数组在内存中起始地址,所以,当数组名作函数参数时,实参加形参之间是地址传递,实参数组名将该数组起始地址传递给形参数组,两个数组共享相同内存单元。编译系统不再为形参数组分配存放单元
21、第61页,9.7 数组作为函数参数,例2 在一个一维数组中存放10个学生C语言程序设计成绩,求平均成绩。,第62页,9.7 数组作为函数参数,例3 用选择法对数组中10个整数按从小到大次序排序。,第63页,9.7 数组作为函数参数,3用多维数组名作函数参数,用多维数组名作函数参数传地址,第64页,9.7 数组作为函数参数,例4 求一个34矩阵中最大元素。,第65页,9.8 局部变量和全局变量,1.8.1,局部变量,1.8.2,全局变量,第66页,9.8.1 局部变量,局部变量在一个函数内部定义变量称为局部变量。它只在本函数范围内有效,只有在本函数中能够使用,在此函数以外不能使用这些变量。,
22、第67页,9.8.1 局部变量,说明:,(,1,)主函数,main,中定义变量只能在主函数中有效。,(,2,)不一样函数中能够使用相同名字变量,它们代表不一样对象,互不干扰。,(,3,)形式参数也是局部变量。,(,4,)在一个函数内部,能够在复合语句中定义变量,这些变量只在本复合语句中有效。,第68页,9.8.2 全局变量,全局变量在函数之外定义变量称为外部变量,外部变量是全局变量。,全局变量能够为本文件中其它函数共用。它有效范围为从定义变量位置开始到根源文件结束。,第69页,9.9 变量存放类别,9.9.1,动态存放方式与静态存放方式,9.9.2,auto变量用static申明局部变量,9.
23、9.3,用static申明局部变量register变量,9.9.4,register变量,9.9.5,用extern申明外部变量变量,第70页,9.9.1 动态存放方式与静态存放方式,从变量作用域(即从空间)来分,能够把变量分为,全局变量,与,局部变量,。,从变量值存在时间(即生存期)来分,能够把变量分为,静态存放方式,和,动态存放方式,。,第71页,9.9.1 动态存放方式与静态存放方式,静态存放方式指在程序运行期间分配固定存放空间方式。,动态存放方式是在程序运行期间依据需要进行动态分配存放空间方式。,第72页,9.9.1 动态存放方式与静态存放方式,内存中提供用户使用存放空间,这个存放空间
24、分为,3,个部分:,1,程序区,2,静态存放区,3,动态存放区,第73页,9.9.1 动态存放方式与静态存放方式,在动态存放区中存放以下数据:,(,1,)函数形式参数。,(,2,)自动变量(未加,static,申明局部变量)。,(,3,)函数调用时保护现场和返回地址等。,第74页,9.9.1 动态存放方式与静态存放方式,在,C,语言中数据存放类别分为两大类:静态存放类和动态存放类。详细包含,4,种:自动(,auto,)、静态(,static,)、存放器(,register,)和外部(,extern,)。依据变量存放类别,能够确定变量作用域和生存期。,第75页,9.9.2 auto变量,自动变量
25、函数中局部变量不说明存放类别或者是用,auto,说明类别变量称为自动变量。,第76页,9.9.3 用static申明局部变量,静态局部变量使用,static,说明其类别局部变量称为静态局部变量。,第77页,9.9.3 用static申明局部变量,静态局部变量在函数调用结束后,函数中局部变量值不消失,保留原值,即不释放所占用存放单元,在下一次调用函数时,静态局部变量已经有值,静态局部变量只初始化一次。,第78页,9.9.3 用static申明局部变量,静态局部变量与自动局部变量比较,:,(,1,)静态局部变量属于静态存放类别,在静态区内分配存放单元,在程序整个运行期间都不释放所占用空间。而自动变
26、量属于动态存放类别,在动态区内分配存放单元,在函数调用结束后释放所占用空间。,第79页,9.9.3 用static申明局部变量,(,2,)静态局部变量只在编译时赋初值一次,以后没次调用函数时不再初始化变量,从而保留了上一次函数调用结束时值。而自动变量不是在编译时赋初值,而是在调用函数时初始化变量,每调用一次函数就初始化一次。,第80页,9.9.3 用static申明局部变量,(,3,)静态局部变量不初始化,则编译系统自动对数值型变量赋初值,0,,对字符变量赋初空值。而自动变量不初始化,则它值是一个不确定诃子。这是因为每次函数调用结束后存放单元已经释放,下次调用时又重新另外反陪存放单元,而新分配
27、单元中值是不确定。,第81页,9.9.3 用static申明局部变量,那么,何时使用静态局部变量呢?,(,1,)需要保留函数上一次调用值。,(,2,)假如初始化后,变量只被引用而不改变其值,这时使用静态局部变量比较方便,以免每次调用时重新赋值。,第82页,9.9.3 用static申明局部变量,例1 分析以下2个程序。,第83页,9.9.3 用static申明局部变量,例,2,打印,1,到,5,阶乘值。,第84页,9.9.4 register变量,例,1,使用存放器变量,分别求,1!,,,2!,,,3!,,,4!,和,5!,值。,第85页,9.9.5,用extern申明外部变量变量,1,在一个
28、文件内申明外部变量,假如外部变量不在文件开头定义,其有效作用范围只限于定义处到文件结束。假如在定义点之前函数想引用该外部变量,则应在引用之前用,extern,对该变量做外部变量申明。,第86页,9.9.5,用extern申明外部变量变量,例1 用extern申明外部变量,扩展它在程序文件中作用域。,第87页,9.9.5,用extern申明外部变量变量,2,在多个文件程序中申明外部变量,假如一个程序包含,2,个以上文件,在两个文件中都要用到同一个外部变量,Num,,不能分别在两个文件中各自定义一个外部变量,Num,,不然在进行成许链接时会出现“重复定义”错误。正确做法是,在任一个文件中定义外部变
29、量,Num,,而在另一个文件中用,extern,对,Num,变量作外部变量申明。即,extern Num;,第88页,9.9.5,用extern申明外部变量变量,例,2,用,extern,将外部变量作用域扩展到其它文件。给定,b,值,输入,a,和,m,,求,a,b,和,a,m,值。,第89页,9.10 工程文件,有程序是由多个源文件组成,怎样组装这些文件呢?首先应创建一个工程文件,然后分别编译各个源程序,最终链接生成可执行程序。,第90页,9.11,编译预处理,教学方法,:,为了锻炼学生自学能力,这一节内容要求学生自学掌握。,第91页,本章小结,学习了本章,要求掌握以下知识:,1,形式参数、实际参数、函数返回值、定义函数、调用函数等基本概念。,2设计无参函数和有参函数调用程序。,3设计函数嵌套调用程序。,4,设计函数递归调用程序。,5,数组作为函数参数。,6,全局变量与局部变量。,7,了解变量存放类别,详细包含自动(auto)、静态(static)、存放器(register)和外部(extern)。,8,工程文件。,9,编译预处理。,第92页,






