第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
1.函数调用的一般形式 前面己经说过,在程序中是通过对函数的调用来执行函数体的。对无参函数调用时则无 实际参数表。实际参数表中的参数可以是常数、变量或其它构造类型数据及表达式,各实参之 间用逗号分隔。 C语言中,函数调用的一般形式为: 函数名(实际参数表) 2.函数调用的方式 在C语言中,可以用以下几种方式调用函数: (1)函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达 式的运算。这种方式要求函数是有返回值的。例如:z=sum(xy)是一个赋值表达式,把sum的 返回值赋予变量z。 (2)函数语句:函数调用的一般形式加上分号即构成函数语句。例如:Hlo(): sum(xy)都是以函数语句的方式调用函数。 (3)函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回 值作为实参进行传送,因此要求该函数必须是有返回值的。例如:printf("%d",sum(Ky)即是 把sum调用的返回值又作为printf函数的实参来使用的。 例如:将代码清单8.4中的部分代码进行修改: 原代码: main( int xv.z: scanf"%d%) z=sum(x.y): printf"z=%dn"z) 修改后代码: main() int x.y scanf"%d%d",&x&y). printf("%dn",sum(x.y).); 3.被调用函数的声明和函数原型 在主调函数中周用某函数之前应付该被调函数讲行声明,这与使用恋量之前要先讲行恋 量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值 的类型,以便在主调函数中按出种类型对返回情作相应的处理。 其一般形式为: 类型说明符被调函数名(类型形参,类型形参…: 或为:
1.函数调用的一般形式 前面已经说过,在程序中是通过对函数的调用来执行函数体的。对无参函数调用时则无 实际参数表。实际参数表中的参数可以是常数、变量或其它构造类型数据及表达式,各实参之 间用逗号分隔。 C语言中,函数调用的一般形式为: 函数名(实际参数表) 2.函数调用的方式 在C语言中,可以用以下几种方式调用函数: (1)函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达 式的运算。这种方式要求函数是有返回值的。例如:z=sum(x,y)是一个赋值表达式,把 sum 的 返回值赋予变量 z。 (2)函数语句:函数调用的一般形式加上分号即构成函数语句。例如:Hello(); sum(x,y);都是以函数语句的方式调用函数。 (3)函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回 值作为实参进行传送,因此要求该函数必须是有返回值的。例如:printf("%d",sum(x,y));即是 把 sum 调用的返回值又作为 printf 函数的实参来使用的。 例如:将代码清单 8.4 中的部分代码进行修改: 原代码: main() { int x,y,z; scanf("%d%d",&x,&y); z=sum(x,y); printf("z=%d\n",z); } 修改后代码: main() { int x,y; scanf("%d%d",&x,&y); printf("%d\n", sum(x,y);); } 3.被调用函数的声明和函数原型 在主调函数中调用某函数之前应对该被调函数进行声明,这与使用变量之前要先进行变 量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值 的类型,以便在主调函数中按此种类型对返回值作相应的处理。 其一般形式为: 类型说明符 被调函数名(类型 形参,类型 形参…); 或为:
类型说明符被调函数名(类型,类型…): 括号内给出了形参的类型和形参名,或只给出形参类型。这便于编译系统进行检错,以 防止可能出现的错误。例如例8.4中代码清单8.5,main函数中对sum函数的说明为: int sum(int a,int b): 或写为 int sum(int int) C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数说明。 (1)如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调 用。这时系统将自动对被调函数返回值按整型处理。 (2)当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调 数再作说明而直接调用。例如例8.4中代码清单8.4,函数sum的定义放在main函数之前,因 此可在main函数中省去对sum函数的函数说明int sum(inta.intb)。 (3)如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主 调函数中,可不再对被调函数作说明。 8.4函数的特殊调用方式 8.4.1函数的嵌套调用 C语言中各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是C语言允 许在一个函数的定义中出现对另一个函数的调用。这样就出现了函数的嵌套调用。即在被调函 数中又调用其它函数。其关系可表示如图8.2所示。图中表示了两层嵌套的情形。其执行过程 是:执行main函数中调用函数A的语句时,即转去执行函数A,在函数A中调用函数B时, 又转去执行函数B,函数B执行完毕返回函数A的断点继续执行,函数A执行完毕返回 mam函数的断点继续执行。 main函复 调用函数 图82函数嵌套调用示意图 【例8.5】利用函数嵌套实现程序设计,求1!+2!++10!。 算法设计:编写求阶乘的函数fact: 编写求和的函数sum(其中嵌套调用函数fact): 编写主函数(其中调用函数sum): 代码清单8.6: #include "stdio h" int fact(int n)
类型说明符 被调函数名(类型,类型…); 括号内给出了形参的类型和形参名,或只给出形参类型。这便于编译系统进行检错,以 防止可能出现的错误。例如例 8.4 中代码清单 8.5,main 函数中对 sum 函数的说明为: int sum(int a,int b); 或写为: int sum(int,int); C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数说明。 (1)如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调 用。这时系统将自动对被调函数返回值按整型处理。 (2)当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函 数再作说明而直接调用。例如例 8.4 中代码清单 8.4,函数 sum 的定义放在 main 函数之前,因 此可在 main 函数中省去对 sum 函数的函数说明 int sum(int a,int b)。 (3)如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主 调函数中,可不再对被调函数作说明。 8.4 函数的特殊调用方式 8.4.1 函数的嵌套调用 C语言中各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是C语言允 许在一个函数的定义中出现对另一个函数的调用。这样就出现了函数的嵌套调用。即在被调函 数中又调用其它函数。其关系可表示如图 8.2 所示。图中表示了两层嵌套的情形。其执行过程 是:执行 main 函数中调用函数 A 的语句时,即转去执行函数 A,在函数 A 中调用函数 B 时, 又转去执行函数 B,函数 B 执行完毕返回函数 A 的断点继续执行,函数 A 执行完毕返回 main 函数的断点继续执行。 图 8.2 函数嵌套调用示意图 【例 8.5】利用函数嵌套实现程序设计,求 1!+2!+…+10!。 算法设计:编写求阶乘的函数 fact; 编写求和的函数 sum(其中嵌套调用函数 fact); 编写主函数(其中调用函数 sum); 代码清单 8.6: #include "stdio.h" int fact(int n) main 函数 调用函数 A 结束 函数 A 调用函数 B 函数 B
int fl,i;, for=1:in:it+)) ffi: return f. int sum(int h) int is=0: for(i=1.i<=h;i++) s=s+fact(i), return s: main( int s; s=sum(10): printf"s=%dn",s) 运行结果:3=4037913。 在程序中,函数fact和sum均为整型,都在主函数之前定义,故不必再在主函数中对 fact和sum加以说明。在主程序中,调用函数sum,将实参10传递给形参h,即h=l0。在函数 sum中利用循环结构调用函数fact共l0次,将实参i从1到10传递给形参n,即 n=1,2,3,…,10后,分别返回1!,2!,3!,,10!给函数sum,函数sum对返回 值求和,然后返回结果给函数main后输出。 8.4.2函数的递归调用 一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。C语言允 许函数的递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身, 每调用一次就进入新的一层。为了防止递归调用无终止地进行,必须在函数内有终止递归调 用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。下 面举例说明递归调用的执行过程。 【例8.6】利用函数递归调用实现程序设计,求n!。 算法设计:编写求阶乘的函数fact: 判断n=1时,返回值1: 判断n≥2时,函数fact调用n*fact(n-l) ④编写主函数调用函数fact: 调用过程如图8.3所示:
{ int f=1,i; for(i=1;i<=n;i++) f=f*i; return f; } int sum(int h) { int i,s=0; for(i=1;i<=h;i++) s=s+fact(i); return s; } main() { int s; s=sum(10); printf("s=%d\n",s); } 运行结果:s=4037913。 在程序中,函数 fact 和 sum 均为整型,都在主函数之前定义,故不必再在主函数中对 fact 和 sum 加以说明。在主程序中,调用函数 sum,将实参 10 传递给形参 h,即 h=10。在函数 sum 中利用循环结构调用函数 fact 共 10 次,将实参 i 从 1 到 10 传递给形参 n,即 n=1,2,3,…,10 后,分别返回 1!,2!,3!,…,10!给函数 sum,函数 sum 对返回 值求和,然后返回结果给函数 main 后输出。 8.4.2 函数的递归调用 一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。C语言允 许函数的递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身, 每调用一次就进入新的一层。为了防止递归调用无终止地进行,必须在函数内有终止递归调 用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。下 面举例说明递归调用的执行过程。 【例 8.6】利用函数递归调用实现程序设计,求 n!。 算法设计:编写求阶乘的函数 fact; 判断 n=1 时,返回值 1; 判断 n≥2 时,函数 fact 调用 n*fact(n-1) ④ 编写主函数调用函数 fact; 调用过程如图 8.3 所示:
s=5*fac(4) 5*24 图83函数递归调用示意图 代码清单8.7: #include”stdio.h" int fact(intn) if(n=1) return 1; else retumn n*fact(n-1) int s; s=fact(5): printf"s=%dn"s) 运行结果:s=120。 8.5数组作为函数参数 数组可以作为函数的参数使用,进行数据传送。数组用作函数参数有两种形式,一种是 把数组元素(下标变量)作为实参使用:另一种是把数组名作为函数的形参和实参使用。 1.数组元素作函数实参 数组元素就是下标变量,它与普通变量并无区别。因此它作为函数实参使用与普通变量 是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值 传送。 【例8.7】判别一个数组中各元素的值,若大于零则输出该值,若小于等于零则输出0值。 代码清单8.8 include"stdio.h" void nzp(imtv) i讽v>0) printf"%d"v)
图 8.3 函数递归调用示意图 代码清单 8.7: #include "stdio.h" int fact(int n) { if(n==1) return 1; else return n*fact(n-1); } main() { int s; s=fact(5); printf("s=%d\n",s); } 运行结果:s=120。 8.5 数组作为函数参数 数组可以作为函数的参数使用,进行数据传送。数组用作函数参数有两种形式,一种是 把数组元素(下标变量)作为实参使用;另一种是把数组名作为函数的形参和实参使用。 1.数组元素作函数实参 数组元素就是下标变量,它与普通变量并无区别。因此它作为函数实参使用与普通变量 是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值 传送。 【例 8.7】判别一个数组中各元素的值,若大于零则输出该值,若小于等于零则输出 0 值。 代码清单 8.8: #include "stdio.h" void nzp(int v) { if(v>0) printf("%d ",v); s=5*fact(4) 4*fact(3) 3*fact(2) 2*fact(1) 1 2*1 3*2 4*6 f=5*24
else print mainO int a[5].i printf"请输入五个数据:方 for(i=0.i<5.i++) scanf"%d"&ai) nzp(a[i]) 运行结果 回'P八教学c酒言教材线巧代网\一回冈 2 3 Press any key to continue 2.数组名作函数参数 函数定义时,形参应写成如下的形式: 类型函数名称(类型标识符数组名称) 例如:void fun(inta) 函数调用时,实参应写成如下的形式: 函数名称(数组名称) 例如:fun(a) 说明: (1)函数定义时,数组名作为形参,需要添加类型标识符进行定义,不需要定义数组 固定长度,但必须加上中括号0,用以表示形参是数组。 (2)函数调用时,数组名作为实参,数组在函数调用之前已被定义,不需要添加类型 标识符再进行定义,不需要定义数组固定长度,也不需要加上中括号。 【例88】数组中存放了一名学生5门课程的成绩,求平均成绩。 代码清单8.9: #include "stdio h" float aver(float a[) inti, float av,s=a[0]: for(i=1i<5.i++) s=s+a[i]:
else printf("%d ",0); } main() { int a[5],i; printf("请输入五个数据:"); for(i=0;i<5;i++) { scanf("%d",&a[i]); nzp(a[i]); } } 运行结果: 2.数组名作函数参数 函数定义时,形参应写成如下的形式: 类型 函数名称(类型标识符 数组名称[]) 例如:void fun(int a[]) 函数调用时,实参应写成如下的形式: 函数名称(数组名称) 例如:fun(a) 说明: (1)函数定义时,数组名作为形参,需要添加类型标识符进行定义,不需要定义数组 固定长度,但必须加上中括号[],用以表示形参是数组。 (2)函数调用时,数组名作为实参,数组在函数调用之前已被定义,不需要添加类型 标识符再进行定义,不需要定义数组固定长度,也不需要加上中括号[]。 【例 8.8】数组中存放了一名学生 5 门课程的成绩,求平均成绩。 代码清单 8.9: #include "stdio.h" float aver(float a[]) { int i; float av,s=a[0]; for(i=1;i<5;i++) s=s+a[i];