第8章函数 【学习目标】 1.掌握函数的定义及一般调用形式。 2.掌握函数的嵌套调用和递归调用方法 3.掌握数组作为函数参数的应用。 4.掌握函数中变量存储类别及作用域。 5.掌握内部函数与外部函数的区别 8.1函数概述 前面章节中介绍过,C源程序是由函数组成的且只有一个主函数maiO,但实用程序往 往由多个函数组成。函数是C源程序的基本模块,通过对函数模块的调用实现特定的功能。用 户把自己的法绵成·个个相对独立的函数模块,然后用调用的方法来使用函数。由于采 用了函数模块式的结构,℃语言易于实现结构化程序设计。使程序的层次结构清晰,便于程 序的编写、阅读、调试。mai函数是主函数,它可以调用其它函数,而不允许被其它函数调用。 因此,C程序的执行总是从main函数开始,完成对其它函数的调用后再返回到main函数, 最后由main函数结束整个程序。 在C语言中可从不同的角度对函数分类,从函数定义的角度看,函数可分为库函数和用 户定义函数两种。 1,库函数 由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函 数原型的头文件即可在程序中直接调用。在前面各章中用到的 printf、scanf、getchar、putchar、gets、puts等函数均属此类,都需要加上头文件#include stdio.h"。 【例81】求√F的值。 代码清单8.1: cude"stdio.h include"math main() int x doubley. scanf"%d"&x) y=sgrt(x): printn"y) 运行结果:输入5,输出2.236068。 说明:一定要加上头文件include"math.h",math.h是数学头文件,sqrt函数是其中之一, 其功能是开方,C系统中提供很多库函数,具体见附录
第 8 章 函数 【学习目标】 1.掌握函数的定义及一般调用形式。 2.掌握函数的嵌套调用和递归调用方法。 3.掌握数组作为函数参数的应用。 4.掌握函数中变量存储类别及作用域。 5.掌握内部函数与外部函数的区别。 8.1 函数概述 前面章节中介绍过,C源程序是由函数组成的且只有一个主函数 main(),但实用程序往 往由多个函数组成。函数是C源程序的基本模块,通过对函数模块的调用实现特定的功能。用 户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。由于采 用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结构清晰,便于程 序的编写、阅读、调试。main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。 因此,C程序的执行总是从 main 函数开始,完成对其它函数的调用后再返回到 main 函数, 最后由 main 函数结束整个程序。 在C语言中可从不同的角度对函数分类,从函数定义的角度看,函数可分为库函数和用 户定义函数两种。 1.库函数 由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函 数 原 型 的 头 文 件 即 可 在 程 序 中 直 接 调 用 。 在 前 面 各 章 中 用 到 的 printf 、scanf、getchar、putchar 、gets、puts 等函数均属此类,都需要加上头文件#include "stdio.h"。 【例 8.1】求 的值。 代码清单 8.1: #include "stdio.h" #include "math.h" main() { int x; double y; scanf("%d",&x); y=sqrt(x); printf("%lf\n",y); } 运行结果:输入 5,输出 2.236068。 说明:一定要加上头文件#include "math.h",math.h 是数学头文件,sqrt 函数是其中之一, 其功能是开方,C系统中提供很多库函数,具体见附录
2.自定义函数 自定义函数是由用户按需要自己编写的函数。对于用户自定义函数,不仅要在程序中定 义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。本 章我们主要介绍自定义函数的定义和调用方法。 8.2函数定义 根据自定义函数是否有返回值和是否有参数,将函数定义分为以下四种形式。 1.无返回值无参数定义形式 void函数名() 函数体: 其中void和函数名称为函数头。函数名是由用户定义的标识符,函数名后有一个空括号, 其中无参数,但括号不可少。}中的内容称为函数体。在很多情况下函数都不要求有返回值和 参数,此时函数类型符可以写为void,void代表函数无类型,即无返回值。我们可以将前面 介绍过的程序修改为自定义函数,代码如下。 void Hello() printf ("Hello) 这里,只把main改为Hello作为函数名,其余不变。Hello函数是一个无返回值无参数 函数,当被其它函数调用时,输出字符串Hello world。 2.无返回值有参数定义形式 void函数名(形式参数表列) 函数体: 有参函数比无参函数多了一个内容,即形式参数表列。在形参表中给出的参数称为形式 参数,它们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数 将赋予这些形式参数实际的值。形参既然是变量,必须在形参表中给出形参的类型说明。 【例82】求两个整数的和(利用无返回值有参数形式)。 代码清单82: void sum(int a.int b) s=a+b: printf"s=%dn"s) 这里,形参为a,b,均为整型量。a,b的具体值是由主调函数在调用时传送过来的。程序中
2.自定义函数 自定义函数是由用户按需要自己编写的函数。对于用户自定义函数,不仅要在程序中定 义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。本 章我们主要介绍自定义函数的定义和调用方法。 8.2 函数定义 根据自定义函数是否有返回值和是否有参数,将函数定义分为以下四种形式。 1.无返回值无参数定义形式 void 函数名( ) { 函数体; } 其中 void 和函数名称为函数头。函数名是由用户定义的标识符,函数名后有一个空括号, 其中无参数,但括号不可少。{}中的内容称为函数体。在很多情况下函数都不要求有返回值和 参数,此时函数类型符可以写为 void,void 代表函数无类型,即无返回值。我们可以将前面 介绍过的程序修改为自定义函数,代码如下。 void Hello( ) { printf ("Hello world!\n"); } 这里,只把 main 改为 Hello 作为函数名,其余不变。Hello 函数是一个无返回值无参数 函数,当被其它函数调用时,输出字符串 Hello world。 2.无返回值有参数定义形式 void 函数名(形式参数表列) { 函数体; } 有参函数比无参函数多了一个内容,即形式参数表列。在形参表中给出的参数称为形式 参数,它们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数 将赋予这些形式参数实际的值。形参既然是变量,必须在形参表中给出形参的类型说明。 【例 8.2】求两个整数的和(利用无返回值有参数形式)。 代码清单 8.2: void sum(int a,int b) { int s; s=a+b; printf("s=%d\n",s); } 这里,形参为 a,b,均为整型量。a,b 的具体值是由主调函数在调用时传送过来的。程序中
只需考虑计算求和后,输出结果。 3.有返回值无参数定义形式 类型标识符函数名() 函数体: return表达式: 类型标识符指明了本函数的类型,函数的类型实际上是函数返回值的类型。该类型标识 符与前面介绍的各种说明符相同,rtum语句的作用是把值作为函数的值返回给主调函数。 有返回值的函数中至少应有一个return语句。 【例8.3】求两个整数的和(利用有返回值无参数形式)。 代码清单8.3: int sum( int abs scanf"%d%d"ab) s=a+b, return s: 第一行说明sum函数是一个整型函数,其返回的函数值是一个整数。在sum函数体中的 return语句是把a和b的和s作为函数的值返回给主调函数,在此不需要打印输出。 4.有返回值有参数定义形式 类型标识符函数名(形式参数表列) 函数体: return表达式, 【例8.4】求两个整数的和(利用有返回值有参数形式)。 代码清单84: #include "stdio.h" int sum(int a,int b) int s: s-a+b return s, main(
只需考虑计算求和后,输出结果。 3.有返回值无参数定义形式 类型标识符 函数名( ) { 函数体; return 表达式; } 类型标识符指明了本函数的类型,函数的类型实际上是函数返回值的类型。该类型标识 符与前面介绍的各种说明符相同, return 语句的作用是把值作为函数的值返回给主调函数。 有返回值的函数中至少应有一个 return 语句。 【例 8.3】求两个整数的和(利用有返回值无参数形式)。 代码清单 8.3: int sum() { int a,b,s; scanf("%d%d",&a,&b); s=a+b; return s; } 第一行说明 sum 函数是一个整型函数,其返回的函数值是一个整数。在 sum 函数体中的 return 语句是把 a 和 b 的和 s 作为函数的值返回给主调函数,在此不需要打印输出。 4.有返回值有参数定义形式 类型标识符 函数名(形式参数表列) { 函数体; return 表达式; } 【例 8.4】求两个整数的和(利用有返回值有参数形式)。 代码清单 8.4: #include "stdio.h" int sum(int a,int b) { int s; s=a+b; return s; } main() { int x,y,z;
scanf("%d%d",&x&). z-sum(x.y). printf("z=%dn"z). 运行结果:输入23/,输出z=5。 在C程序中, 一个函数的定义可以放在任意位置,既可放在主函数main之前,也可放 在main之后。也可以把它放在main之后,修改后的程序如代码清单8.5所示。 代码清单8.5: #include"stdio.h" main() int sum(int a.int b) int xyz scanf"%d%d"xy). z=sum(x,y); printf"z=%dn"z). int sum(int a.int b) int s; return s 运行结果:输入23/,输出2=5。 现在我们可以从函数定义、函数说明及函数调用的角度来分析整个程序,从中进一步了 解函数的各种特点。程序的第10行至第15行为sum函数定义。进入主函数后,因为准备调用 sum函数,故先对sum函数进行说明(程序第4行)。函数定义和函数说明并不是一回事,在后 面还要专门讨论。可以看出函数说明与函数定义中的函数头部分相同,但是末尾要加分号。程 序第7行为调用sum函数,并把实参xy中的值传送给sum的形参ab。sum函数执行的结果s 将返回给变量z,最后由主函数输出z的值。 8.3函数的一般调用方式 8.3.1形式参数和实际参数 前面已经介绍过,函数的参数分为形参和实参两种。在本小节中,进一步介绍形参、实参 的特点和两者的关系。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则 不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功 能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主 调函数向被调函数的数据传送。函数的形参和实参具有以下特点: 1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元
scanf("%d%d",&x,&y); z=sum(x,y); printf("z=%d\n",z); } 运行结果:输入 2 3↙,输出 z=5。 在C程序中,一个函数的定义可以放在任意位置,既可放在主函数 main 之前,也可放 在 main 之后。也可以把它放在 main 之后,修改后的程序如代码清单 8.5 所示。 代码清单 8.5: #include "stdio.h" main() { int sum(int a,int b); int x,y,z; scanf("%d%d",&x,&y); z=sum(x,y); printf("z=%d\n",z); } int sum(int a,int b) { int s; s=a+b; return s; } 运行结果:输入 2 3↙,输出 z=5。 现在我们可以从函数定义、函数说明及函数调用的角度来分析整个程序,从中进一步了 解函数的各种特点。程序的第 10 行至第 15 行为 sum 函数定义。进入主函数后,因为准备调用 sum 函数,故先对 sum 函数进行说明(程序第 4 行)。函数定义和函数说明并不是一回事,在后 面还要专门讨论。可以看出函数说明与函数定义中的函数头部分相同,但是末尾要加分号。程 序第 7 行为调用 sum 函数,并把实参 x,y 中的值传送给 sum 的形参 a,b。sum 函数执行的结果 s 将返回给变量 z,最后由主函数输出 z 的值。 8.3 函数的一般调用方式 8.3.1 形式参数和实际参数 前面已经介绍过,函数的参数分为形参和实参两种。在本小节中,进一步介绍形参、实参 的特点和两者的关系。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则 不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功 能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主 调函数向被调函数的数据传送。函数的形参和实参具有以下特点: 1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元
因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。 2.实参可可以是常量、变量、表大式、函数第,无论实参是何种类型的量,在讲行函数调用村, 它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值、输入等办法使实参 获得确定值 3.实参和形参在数量上、类型上和顺序上应严格一致,否则会发生类型不匹配的错误。 4.函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值 反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。例 8.4中参数传递如图8.1所示。 2 3 图8.1形参和实参值传递示意图 8.3.2函数返回值 函数的值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的值。 如调用例8.4中的sum函数取得两数之和。对函数返回值有以下一些说明: l.函数的值只能通过return语句返回主调函数。 return语句的一般形式为: return表达式: 或者为: return(表达式): 该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个return语句, 但每次调用只能有一个return语句被执行,因此只能返回一个函数值。 2.函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类型为准, 自动讲行类型转换。 3.如函数值为整型,在函数定义时可以省去类型说明。 4.不返回函数值的函数,可以明确定义为“空类型”,类型说明符为“void”。一旦函数被 定义为空类型后,就不能在主调函数中使用被调函数的函数值了。如例8.2中函数sum并不 向主函数返函数值,因此可定义为: void sum(int a,int b) 如果,在主函数中写下述语句z=sum(xy就是错误的。 8.3.3函数调用方式
因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。 2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时, 它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值、输入等办法使实参 获得确定值。 3.实参和形参在数量上、类型上和顺序上应严格一致,否则会发生类型不匹配的错误。 4.函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值 反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。例 8.4 中参数传递如图 8.1 所示。 图 8.1 形参和实参值传递示意图 8.3.2 函数返回值 函数的值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的值。 如调用例 8.4 中的 sum 函数取得两数之和。对函数返回值有以下一些说明: 1.函数的值只能通过 return 语句返回主调函数。 return 语句的一般形式为: return 表达式; 或者为: return (表达式); 该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个 return 语句, 但每次调用只能有一个 return 语句被执行,因此只能返回一个函数值。 2.函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类型为准, 自动进行类型转换。 3.如函数值为整型,在函数定义时可以省去类型说明。 4.不返回函数值的函数,可以明确定义为“空类型”,类型说明符为“void”。一旦函数被 定义为空类型后,就不能在主调函数中使用被调函数的函数值了。如例 8.2 中函数 sum 并不 向主函数返函数值,因此可定义为: void sum(int a,int b) { … … } 如果,在主函数中写下述语句 z=sum(x,y);就是错误的。 8.3.3 函数调用方式 x 2 y 3 a 2 b 3