1、主讲教师:代祖华主讲教师:代祖华 程序设计基础4.1 4.1 函数的定义函数的定义4.2 4.2 函数的调用函数的调用4.3 4.3 变量的作用域与存储特性变量的作用域与存储特性4.4 4.4 函数间数据的传递函数间数据的传递4.5 4.5 递归函数递归函数4.6 4.6 综合编程举例综合编程举例4.1 函数的定义函数的定义从函数使用的角度来看,从函数使用的角度来看,C C语言的函数可以分为两语言的函数可以分为两类:标准库函数和用户自定义函数。本章介绍后类:标准库函数和用户自定义函数。本章介绍后者。者。一一.标准库函数与头文件标准库函数与头文件1.Turbo C系统提供了系统提供了400多个标
2、准库函数多个标准库函数(参见附录五),按功能可以分为:(参见附录五),按功能可以分为:(1)类型转换函数类型转换函数,(2)字符判别与转换函数字符判别与转换函数,(3)字字符串处理函数符串处理函数,(4)标准标准I/O函数函数,(5)文件管理函文件管理函数数,(6)数学运算函数等。数学运算函数等。它们的执行效率高,用户需要时,可在程序它们的执行效率高,用户需要时,可在程序中直接进行调用。中直接进行调用。C语言库函数所用到的常量、外部变量、函语言库函数所用到的常量、外部变量、函数类型和参数说明,都在相应的头文件(扩展数类型和参数说明,都在相应的头文件(扩展名为名为.h)中声明,这些文件通常存放在
3、系统目)中声明,这些文件通常存放在系统目录录tcinclude。如。如:2.头文件头文件(1)stdio.h文件:文件:标准输入输出函数所用的常量、标准输入输出函数所用的常量、结构、宏定义、函数的类型、参数的个数与结构、宏定义、函数的类型、参数的个数与类型的描述。类型的描述。(2)math.h文件:文件:与数学函数有关的常量、结构与数学函数有关的常量、结构及相应的函数类型和参数描述。及相应的函数类型和参数描述。(3)string.h文件:文件:与字符串操作函数有关的常量、与字符串操作函数有关的常量、结构以及相应的函数类型和参数描述。结构以及相应的函数类型和参数描述。二二.用户自定义函数用户自定
4、义函数1.函数分类函数分类(从函数的形式看从函数的形式看)无参函数无参函数有参函数有参函数函数定义时无参数说明函数定义时无参数说明调用无参函数一般用来执行指定的调用无参函数一般用来执行指定的一组操作一组操作主调函数不传送数据给被调函数主调函数不传送数据给被调函数函数定义时定义了一个或一个以上函数定义时定义了一个或一个以上的参数的参数调用时将要处理的数据传送给被调用时将要处理的数据传送给被调函数调函数定义形式二:定义形式二:函数值类型名函数值类型名 函数名(形式参数类型及参数说明)函数名(形式参数类型及参数说明)数据说明部分数据说明部分 语句语句 定义形式一:定义形式一:函数值类型名函数值类型名
5、 函数名(形式参数列表)函数名(形式参数列表)形式参数说明形式参数说明数据说明部分数据说明部分 语句语句 2.函数定义形式函数定义形式ANSI风格风格如如:int max(n1,n2)int n1,n2;如如:int max(intn1,int n2)3.函数由函数首部与函数体两部分构成。函数由函数首部与函数体两部分构成。(1)函数首部)函数首部函数首部包括函数值类型、函数名、参数类型及参数说函数首部包括函数值类型、函数名、参数类型及参数说明明.函数值类型指定所定义函数返回值的类型,可以是简函数值类型指定所定义函数返回值的类型,可以是简单数据类型、单数据类型、void类型或构造类型等。类型或构
6、造类型等。当函数值类型为当函数值类型为void时,表示函数无返回值,相当于其时,表示函数无返回值,相当于其它语言的过程。当函数值类型为它语言的过程。当函数值类型为int时,可省略其类型的时,可省略其类型的说明,建议不使用缺省形式类型说明说明,建议不使用缺省形式类型说明.函数名是函数的标识符,遵循函数名是函数的标识符,遵循C语言标识符的命名规则,语言标识符的命名规则,区分大小写。区分大小写。为了与调用函数提供的实际参数区别开,将函数定义中的参数表称为形式参数表,简称形参,处在函数名后简称形参,处在函数名后的一对圆括号中。要特别注意的是,无论函数是否有形的一对圆括号中。要特别注意的是,无论函数是否
7、有形式参数,函数名后的圆括号不可省,并且圆括号之后不式参数,函数名后的圆括号不可省,并且圆括号之后不能接能接“;”。函数体结束在函数体结束在“”括号处。括号处。(2)函数体)函数体函数首部之后的花括号函数首部之后的花括号“”部分为函数体部分为函数体。函数体内数据说明部分在前,执行语句部分在后。函数体内数据说明部分在前,执行语句部分在后。函数体中说明的变量是该函数调用时有效的局部变函数体中说明的变量是该函数调用时有效的局部变量,执行语句是实际生成命令代码的部分。量,执行语句是实际生成命令代码的部分。函数的功能由函数体内的各个语句的执行来实现。函数的功能由函数体内的各个语句的执行来实现。空函数空函
8、数定义时无参数,定义时无参数,内为空内为空被调用时,不执行任何操作就立即返回。例如被调用时,不执行任何操作就立即返回。例如:void print_report(void)4.2 函数的调用函数的调用在在C语言的一般函数体中,可以包含对其它函数的调用,语言的一般函数体中,可以包含对其它函数的调用,称为函数的嵌套调用,甚至包含对自身的调用,称为函数称为函数的嵌套调用,甚至包含对自身的调用,称为函数的递归调用。的递归调用。一个函数一旦被定义,就可在程序的其它函数中使用它,一个函数一旦被定义,就可在程序的其它函数中使用它,这个过程称为函数调用。这个过程称为函数调用。一一.函数的一般调用与声明函数的一般
9、调用与声明函数名函数名(实际参数列表)实际参数列表)1.函数调用形式函数调用形式函数调用的一般形式:函数调用的一般形式:实际参数简称为实参,参数之间以实际参数简称为实参,参数之间以 逗号分隔。逗号分隔。函数调用时,实参与形参应保持个数、次序及类型函数调用时,实参与形参应保持个数、次序及类型的一致性,以确保实参与形参之间数据的正确传递。的一致性,以确保实参与形参之间数据的正确传递。实际参数可以是表达式、常量、变量(调用时必须实际参数可以是表达式、常量、变量(调用时必须有确定的值或确定的地址)。有确定的值或确定的地址)。形式参数必须为变量。形式参数必须为变量。当实际参数的个数、次序、类型与对应形式
10、参数的个当实际参数的个数、次序、类型与对应形式参数的个数、次序、类型不一致时,系统并不提示错误,后果却数、次序、类型不一致时,系统并不提示错误,后果却难以预测。难以预测。2.格式说明格式说明3.C语言中函数调用的三种形式语言中函数调用的三种形式:语句调用语句调用表达式调用表达式调用函数参数调用函数参数调用在函数调用后加在函数调用后加“;”,构成一个语句。,构成一个语句。调用函数的目的可能是执行一个动作或调用函数的目的可能是执行一个动作或完成特定的功能。完成特定的功能。大多数函数的调用形式。大多数函数的调用形式。被调用函数执行的结果为调用函数提供被调用函数执行的结果为调用函数提供一个值。一个值。
11、调用函数通过表达式接收值。调用函数通过表达式接收值。被调函数作为某个函数的一个参数。被调函数作为某个函数的一个参数。main()/*主函数中采用函数参数调用形式调用函数主函数中采用函数参数调用形式调用函数max*/int x,y,z,m;scanf(%d,%d,%d,&x,&y,&z);printf(max=%dn,max(max(x,y),z);/*内层函数内层函数max2的值作为外层函数的值作为外层函数max2的实参,整个的实参,整个函数函数max2的值又作函数的值又作函数printf的的实参的的实参*/例例 函数参数调用形式调用函数函数参数调用形式调用函数max:int max(int
12、n1,int n2)/*求两个数中较大者求两个数中较大者*/int y;y=(n1n2)?n1:n2;return(y);4.函数声明函数声明(函数说明)函数说明)调用用户自定义函数时,一般调用函数和被调用函数应在同一个调用用户自定义函数时,一般调用函数和被调用函数应在同一个文件中,在调用函数中对被调用函数返回值的类型、函数名称、函文件中,在调用函数中对被调用函数返回值的类型、函数名称、函数形式参数的类型进行说明,这种说明称为函数声明。数形式参数的类型进行说明,这种说明称为函数声明。函数声明的一般形式如下:函数声明的一般形式如下:类型名类型名 函数名(类型函数名(类型1 形参形参1,类型,类型
13、n 形参形参n););或或类型名类型名 函数名(类型函数名(类型1,类型,类型2,类型,类型n););或或类型名类型名 函数名();函数名();函数声明是以语句形式出现的,因此其后有语句结束标记函数声明是以语句形式出现的,因此其后有语句结束标记“;”。若函数定义放在主调函数之前,遵循先定义后调用原则若函数定义放在主调函数之前,遵循先定义后调用原则,函数声明函数声明可以省略可以省略;若函数值的类型为整型或字符型,函数声明可以省略;若函数值的类型为整型或字符型,函数声明可以省略;若在所有函数定义之前,已说明函数类型,函数声明也可省略。若在所有函数定义之前,已说明函数类型,函数声明也可省略。二二.形
14、参、实参与函数调用过程形参、实参与函数调用过程形式参数形式参数(简称形参简称形参)定义函数使用的参数定义函数使用的参数实际参数实际参数(简称实参简称实参)调用函数使用的参数调用函数使用的参数C语言中,采用函数之间的参数传递方式从实参向形参语言中,采用函数之间的参数传递方式从实参向形参传递。传递。C语言函数的参数传递均采用单向值传递方式(或称复语言函数的参数传递均采用单向值传递方式(或称复制方式)。制方式)。单向值传递方式是指在函数调用时,将实参之值传递单向值传递方式是指在函数调用时,将实参之值传递给对应的形式参数,使形参具有与实参相同的值。给对应的形式参数,使形参具有与实参相同的值。当实参是变
15、量的地址值、指针常量或指针变量时,实当实参是变量的地址值、指针常量或指针变量时,实参传递给形参的是地址值,同样是单向值传递方式。参传递给形参的是地址值,同样是单向值传递方式。变量的作用域变量的作用域4.3 4.3 变量的作用域及存储特性变量的作用域及存储特性例例4-4:void f1()int t=2;a*=t;b/=t;void main()int a,b;printf(”Enter a,b:”);scanf(”%d,%d”,&a,&b);f1();/*调用函数调用函数f1()*/printf(”a=%d,b=%d”,a,b);编译程序会提示出错编译程序会提示出错:Undefined sym
16、bol a 和和 Undefined symbol b。为什么为什么?例例4_5:输入长方体的长(:输入长方体的长(l)、宽()、宽(w)、)、高(高(h),求长方体体积及正、侧、顶三个面的面),求长方体体积及正、侧、顶三个面的面积。积。/*功能:利用全局变量计算长方体的体积及三个面的面功能:利用全局变量计算长方体的体积及三个面的面积积*/int s1,s2,s3;int vs(int a,int,b,int c)int v;v=a*b*c;s1=a*b;s2=b*c;s3=a*c;return v;main()int v,l,w,h;printf(ninput length,width an
17、d height:);scanf(%d%d%d,&l,&w,&h);v=vs(l,w,h);printf(v=%d,s1=%d,s2=%d,s3=%dn,v,s1,s2,s3);getch();二、二、变量的存储特性(反映变量的生存期)变量的存储特性(反映变量的生存期)变量的存储空间的分配方式,不同变量的存储空间的分配方式,不同存储方式的变量对不同的生命周期。存储方式的变量对不同的生命周期。n静态存储变量静态存储变量生存期为程序执行的整个过程,在该过程中占有固定生存期为程序执行的整个过程,在该过程中占有固定的存储空间,也称永久存储。的存储空间,也称永久存储。n动态存储变量动态存储变量只生存在程
18、序运行的某一段时间内,如只生存在程序运行的某一段时间内,如函数的形参、函数的形参、函数中定义的自动局部变量函数中定义的自动局部变量,只有当程序进入该函数时才,只有当程序进入该函数时才分配存储空间,函数执行完后,变量的存储空间又被释放。分配存储空间,函数执行完后,变量的存储空间又被释放。2动态存储动态存储自动局部变量(又称自动变量)自动局部变量(又称自动变量)(1)定义格式:)定义格式:auto 数据类型数据类型 变量表;变量表;(2)存储特点)存储特点 自动变量属于动态存储方式。在函数中定义的自动变自动变量属于动态存储方式。在函数中定义的自动变量,只在该函数内有效;函数被调用时分配存储空间,调
19、量,只在该函数内有效;函数被调用时分配存储空间,调用结束就释放。用结束就释放。在复合语句中定义的自动变量,只在该复合语句中有在复合语句中定义的自动变量,只在该复合语句中有效;退出复合语句后,也不能再使用,否则将引起错误。效;退出复合语句后,也不能再使用,否则将引起错误。定义而不初始化,则其值是不确定的。如果初始化,定义而不初始化,则其值是不确定的。如果初始化,则赋初值操作是在调用时进行的,且每次调用都要重新赋则赋初值操作是在调用时进行的,且每次调用都要重新赋一次初值。一次初值。变量定义缺省存储类型说明,系统自动将其定义为自变量定义缺省存储类型说明,系统自动将其定义为自动存储类型。动存储类型。3
20、寄存器存储寄存器存储寄存器变量寄存器变量一般情况下,变量的值都是存储在内存中的。一般情况下,变量的值都是存储在内存中的。为提高执行效率,语言允许将局部变量的值存为提高执行效率,语言允许将局部变量的值存放到寄存器中,这种变量就称为寄存器变量。定放到寄存器中,这种变量就称为寄存器变量。定义格式如下:义格式如下:register 数据类型数据类型 变量表;变量表;(1)只有局部变量才能定义成寄存器变量,即)只有局部变量才能定义成寄存器变量,即全局变量不行。全局变量不行。(2)对寄存器变量的实际处理,随系统而异。)对寄存器变量的实际处理,随系统而异。例如,微机上例如,微机上TC 将寄存器变量实际当作自
21、动变量将寄存器变量实际当作自动变量处理。处理。(3)允许使用的寄存器数目是有限的,不能定)允许使用的寄存器数目是有限的,不能定义任意多个寄存器变量。义任意多个寄存器变量。静态局部变量和静态外部变量同属静态存储方式,但静态局部变量和静态外部变量同属静态存储方式,但两者区别较大:两者区别较大:(1)定义的位置不同。静态局部变量在函数内定义,)定义的位置不同。静态局部变量在函数内定义,静态外部变量在函数外定义。静态外部变量在函数外定义。(2)作用域不同。静态局部变量作用域仅限于定义它)作用域不同。静态局部变量作用域仅限于定义它的函数内;虽然生存期为整个源程序,但其它函数是不能的函数内;虽然生存期为整
22、个源程序,但其它函数是不能使用它的。静态外部变量作用域为定义它的源文件内;生使用它的。静态外部变量作用域为定义它的源文件内;生存期为整个源程序,但其它源文件中的函数也是不能使用存期为整个源程序,但其它源文件中的函数也是不能使用它的。它的。(3)初始化处理不同。静态局部变量,仅在第)初始化处理不同。静态局部变量,仅在第1次调次调用它所在的函数时被初始化,当再次调用定义它的函数时,用它所在的函数时被初始化,当再次调用定义它的函数时,不再初始化,而是保留上不再初始化,而是保留上1次调用结束时的值。而静态外次调用结束时的值。而静态外部变量是在函数外定义的,其当前值由最近部变量是在函数外定义的,其当前值
23、由最近1次给它赋值次给它赋值的操作决定。的操作决定。函数间数据传递的的三种形式函数间数据传递的的三种形式:参数传递参数传递return语句语句全局变量全局变量传值方式传值方式:传址方式:传址方式:将被调用函数执行的结果作为函数返回值提将被调用函数执行的结果作为函数返回值提供给调用函数。供给调用函数。调用函数通过表达式接收值。调用函数通过表达式接收值。利用全局变量的全局作用域特性实现数利用全局变量的全局作用域特性实现数据在函数间的传递据在函数间的传递实参的值传递给形参变量(单向实参的值传递给形参变量(单向传递传递)实参的地址传递给形参变量实参的地址传递给形参变量 直接在函数内调用自己为直接递归,
24、通过直接在函数内调用自己为直接递归,通过别的函数调用自己为间接递归。别的函数调用自己为间接递归。void a().a();.void a().b();.void b().a();.递归在解决某些问题递归在解决某些问题中,是一个十分有用的中,是一个十分有用的方法。因为有的问题它方法。因为有的问题它本身就是递归定义的本身就是递归定义的;此此外,它可以使某些看起外,它可以使某些看起来不易解决的问题变得来不易解决的问题变得容易解决,写出的程序容易解决,写出的程序较简短。较简短。由于由于 n!=n*(n-1)!是递归定义是递归定义所以求所以求n!(n-1)!)!(n-1)!(n-2)!)!(n 2)!(
25、n-3)!)!1!的问题,!的问题,根据公式有根据公式有1!=1。再反过来依次求出再反过来依次求出2!,!,3!直到最后直到最后求出求出n!。!。使用递归解决的问题应满足两个基本条件:使用递归解决的问题应满足两个基本条件:(1 1)递归问题的转化。有些问题不能直接求解或)递归问题的转化。有些问题不能直接求解或难以求解,但它可以转化为一个递归问题,这个难以求解,但它可以转化为一个递归问题,这个递归问题相对于原问题简单或更接近解决方法。递归问题相对于原问题简单或更接近解决方法。(2 2)转化的终止条件。原问题到递归问题的转化)转化的终止条件。原问题到递归问题的转化是有条件的、次数是有限的,不能无限次数地转是有条件的、次数是有限的,不能无限次数地转化下去。化下去。这个终止条件也称为边界条件,相当于递推这个终止条件也称为边界条件,相当于递推关系中的初始条件。关系中的初始条件。