靳润昭C语言教程讲义 2001年2月17日 8函数 1概述 函数定义的一般形式 83函数的参数和函数的值 831形式参数和实际参数 832函数的返回值 函数的调用 841函数调用的一般形式 842函数调用的方式 6667 843被调用函数的声明和函数原型 85函数的嵌套调用 函数的递归调用 8.7数组作为函数参数 88局部变量和全局变量 0277 8.81局部变量 882全局变量 9 变量的存储类别 891动态存储方式与静态动态存储方式 892auto变量 2222 893用 statIc声明局部变量 8.94 变量 895用 extern声明外部变量 8函数 81概述 在前面已经介绍过,C源程序是由函数组成的。虽然在前面各章的程序中大都只有一个 主函数main(,但实用程序往往由多个函数组成。函数是C源程序的基本模块,通过对函 数模块的调用实现特定的功能。C语言中的函数相当于其它高级语言的子程序。C语言不仅 提供了极为丰富的库函数(如 Turbo C,MSC都提供了三百多个库函数),还允许用户建立自 己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来 使用函数。可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函 数式语言 由于采用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结构清 晰,便于程序的编写、阅读、调试 在C语言中可从不同的角度对函数分类 1.从函数定义的角度看,函数可分为库函数和用户定义函数两种。 1)库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序 前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用 到 printf、 scanf、 getchar、 putchar、gets、puts、 strcat等函数均属此类。 第1页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第1页 8 函 数........................................................................................................................ 1 8.1 概述............................................................................................................ 1 8.2 函数定义的一般形式................................................................................... 3 8.3 函数的参数和函数的值................................................................................ 4 8.3.1 形式参数和实际参数............................................................................ 4 8.3.2 函数的返回值....................................................................................... 5 8.4 函数的调用................................................................................................. 6 8.4.1 函数调用的一般形式............................................................................ 6 8.4.2 函数调用的方式................................................................................... 6 8.4.3 被调用函数的声明和函数原型.............................................................. 7 8.5 函数的嵌套调用.......................................................................................... 8 8.6 函数的递归调用........................................................................................ 10 8.7 数组作为函数参数..................................................................................... 12 8.8 局部变量和全局变量................................................................................. 17 8.8.1 局部变量............................................................................................ 17 8.8.2 全局变量............................................................................................ 19 8.9 变量的存储类别........................................................................................ 20 8.9.1 动态存储方式与静态动态存储方式..................................................... 20 8.9.2 auto 变量............................................................................................ 21 8.9.3 用 static 声明局部变量........................................................................ 21 8.9.4 register 变量....................................................................................... 22 8.9.5 用 extern 声明外部变量....................................................................... 23 8 函 数 8.1 概述 在前面已经介绍过,C源程序是由函数组成的。虽然在前面各章的程序中大都只有一个 主函数 main(),但实用程序往往由多个函数组成。函数是C源程序的基本模块,通过对函 数模块的调用实现特定的功能。C语言中的函数相当于其它高级语言的子程序。C语言不仅 提供了极为丰富的库函数(如 Turbo C,MS C 都提供了三百多个库函数),还允许用户建立自 己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来 使用函数。可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函 数式语言。 由于采用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结构清 晰,便于程序的编写、阅读、调试。 在C语言中可从不同的角度对函数分类。 1. 从函数定义的角度看,函数可分为库函数和用户定义函数两种。 1) 库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序 前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用 到 printf、scanf、getchar、putchar、gets、puts、strcat 等函数均属此类
靳润昭C语言教程讲义 2001年2月17日 2)用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定 义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能 使用 2.C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为 有返回值函数和无返回值函数两种。 1)有返回值函数:此类函数被调用执行完后将向调用者返回一个执行结果,称为函数 返回值。如数学函数即属于此类函数。由用户定义的这种要返回函数值的函数,必 须在函数定义和函数说明中明确返回值的类型。 2)无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返 回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此 类函数时可指定它的返回为“空类型”,空类型的说明符为 vOl 3.从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种 1)无参函数:函数定义、函数说明及函数调用中均不带参数。主调函数和被调函数之 间不进行参数传送。此类函数通常用来完成一组指定的功能,可以返回或不返回函 数值 2)有参函数:也称为带参函数。在函数定义及函数说明时都有参数,称为形式参数(简 称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)。进行函 数调用时,主调函数将把实参的值传送给形参,供被调函数使用。 4.C语言提供了极为丰富的库函数,这些库函数又可从功能角度作以下分类 1)字符类型分类函数:用于对字符按ASCI码分类:字母,数字,控制字符,分隔符, 大小写字母等 2)转换函数:用于字符或字符串的转换;在字符量和各类数字量(整型,实型等)之间 进行转换;在大、小写之间进行转换 3)目录路径函数:用于文件目录和路径操作 4)诊断函数:用于内部错误检测。 5)图形函数:用于屏幕管理和各种图形功能。 6)输入输出函数:用于完成输入输出功能 7)接口函数:用于与DOS,BI0S和硬件的接口。 8)字符串函数:用于字符串操作和处理。 9)内存管理函数:用于内存管理。 10)数学函数:用于数学函数计算。 11)日期和时间函数:用于日期,时间转换操作 12)进程控制函数:用于进程管理和控制。 13)其它函数:用于其它各种功能 以上各类函数不仅数量多,而且有的还需要硬件知识才会使用,因此要想全部掌握则需 要一个较长的学习过程。应首先掌握一些最基本、最常用的函数,再逐步深入。由于课时关 系,我们只介绍了很少一部分库函数,其余部分读者可根据需要查阅有关手册。 还应该指出的是,在C语言中,所有的函数定义,包括主函数main在内,都是平行的。 也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。但是函数之 间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。函数还可以自己调用自 称为递归调用 main函数是主函数,它可以调用其它函数,而不允许被其它函数调用。因此,C程序 的执行总是从main函数开始,完成对其它函数的调用后再返回到main函数,最后由main 函数结束整个程序。一个C源程序必须有,也只能有一个主函数main 第2页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第2页 2) 用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定 义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能 使用。 2. C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为 有返回值函数和无返回值函数两种。 1) 有返回值函数:此类函数被调用执行完后将向调用者返回一个执行结果,称为函数 返回值。如数学函数即属于此类函数。由用户定义的这种要返回函数值的函数,必 须在函数定义和函数说明中明确返回值的类型。 2) 无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返 回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此 类函数时可指定它的返回为“空类型”, 空类型的说明符为“void”。 3. 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。 1) 无参函数:函数定义、函数说明及函数调用中均不带参数。主调函数和被调函数之 间不进行参数传送。此类函数通常用来完成一组指定的功能,可以返回或不返回函 数值。 2) 有参函数:也称为带参函数。在函数定义及函数说明时都有参数,称为形式参数(简 称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)。进行函 数调用时,主调函数将把实参的值传送给形参,供被调函数使用。 4. C语言提供了极为丰富的库函数,这些库函数又可从功能角度作以下分类。 1) 字符类型分类函数:用于对字符按 ASCII 码分类:字母,数字,控制字符,分隔符, 大小写字母等。 2) 转换函数:用于字符或字符串的转换;在字符量和各类数字量(整型,实型等)之间 进行转换;在大、小写之间进行转换。 3) 目录路径函数:用于文件目录和路径操作。 4) 诊断函数:用于内部错误检测。 5) 图形函数:用于屏幕管理和各种图形功能。 6) 输入输出函数:用于完成输入输出功能。 7) 接口函数:用于与 DOS,BIOS 和硬件的接口。 8) 字符串函数:用于字符串操作和处理。 9) 内存管理函数:用于内存管理。 10) 数学函数:用于数学函数计算。 11) 日期和时间函数:用于日期,时间转换操作。 12) 进程控制函数:用于进程管理和控制。 13) 其它函数:用于其它各种功能。 以上各类函数不仅数量多,而且有的还需要硬件知识才会使用,因此要想全部掌握则需 要一个较长的学习过程。应首先掌握一些最基本、最常用的函数,再逐步深入。由于课时关 系,我们只介绍了很少一部分库函数,其余部分读者可根据需要查阅有关手册。 还应该指出的是,在C语言中,所有的函数定义,包括主函数 main 在内,都是平行的。 也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。但是函数之 间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。函数还可以自己调用自 己,称为递归调用。 main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。因此,C程序 的执行总是从 main 函数开始,完成对其它函数的调用后再返回到 main 函数,最后由 main 函数结束整个程序。一个C源程序必须有,也只能有一个主函数 main
靳润昭C语言教程讲义 2001年2月17日 82函数定义的一般形式 1.无参函数的定义形式 类型标识符函数名0 声明部分 语句 其中类型标识符和函数名称为函数头。类型标识符指明了本函数的类型,函数的类型实 际上是函数返回值的类型。该类型标识符与前面介绍的各种说明符相同。函数名是由用户 定义的标识符,函数名后有一个空括号,其中无参数,但括号不可少。 仆}中的内容称为函数体。在函数体中声明部分,是对函数体内部所用到的变量的类型说 明 在很多情况下都不要求无参函数有返回值,此时函数类型符可以写为 void 我们可以改写一个函数定义: void Helloo printf ("Hello, world \n") 这里,只把main改为 Hello作为函数名,其余不变。 Hello函数是一个无参函数,当 被其它函数调用时,输出 Hello world字符串 2.有参函数定义的一般形式 类型标识符函数名(形式参数表列) 声明部分 语句 有参函数比无参函数多了一个内容,即形式参数表列。在形参表中给出的参数称为形式 参数,它们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数 将赋予这些形式参数实际的值。形参既然是变量,必须在形参表中给出形参的类型说明 例如,定义一个函数,用于求两个数中的大数,可写为: int max(int a, int b if (a>b)return a else return b 第一行说明max函数是一个整型函数,其返回的函数值是一个整数。形参为a,b,均为 整型量。a,b的具体值是由主调函数在调用时传送过来的。在仆}中的函数体内,除形参外没 有使用其它变量,因此只有语句而没有声明部分。在max函数体中的 returm语句是把a(或 b)的值作为函数的值返回给主调函数。有返回值函数中至少应有一个 return语句。 在C程序中,一个函数的定义可以放在任意位置,既可放在主函数main之前,也可放 在main之后 例如: 可把max函数置在main之后,也可以把它放在main之前。修改后的程序如下所示 【例8.1】 第3页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第3页 8.2 函数定义的一般形式 1. 无参函数的定义形式 类型标识符 函数名() {声明部分 语句 } 其中类型标识符和函数名称为函数头。类型标识符指明了本函数的类型,函数的类型实 际上是函数返回值的类型。 该类型标识符与前面介绍的各种说明符相同。函数名是由用户 定义的标识符,函数名后有一个空括号,其中无参数,但括号不可少。 {}中的内容称为函数体。在函数体中声明部分,是对函数体内部所用到的变量的类型说 明。 在很多情况下都不要求无参函数有返回值,此时函数类型符可以写为 void。 我们可以改写一个函数定义: void Hello() { printf ("Hello,world \n"); } 这里,只把 main 改为 Hello 作为函数名,其余不变。Hello 函数是一个无参函数,当 被其它函数调用时,输出 Hello world 字符串。 2. 有参函数定义的一般形式 类型标识符 函数名(形式参数表列) {声明部分 语句 } 有参函数比无参函数多了一个内容,即形式参数表列。在形参表中给出的参数称为形式 参数,它们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数 将赋予这些形式参数实际的值。形参既然是变量,必须在形参表中给出形参的类型说明。 例如,定义一个函数,用于求两个数中的大数,可写为: int max(int a, int b) { if (a>b) return a; else return b; } 第一行说明 max 函数是一个整型函数,其返回的函数值是一个整数。形参为 a,b,均为 整型量。a,b 的具体值是由主调函数在调用时传送过来的。在{}中的函数体内,除形参外没 有使用其它变量,因此只有语句而没有声明部分。在 max 函数体中的 return 语句是把 a(或 b)的值作为函数的值返回给主调函数。有返回值函数中至少应有一个 return 语句。 在C程序中,一个函数的定义可以放在任意位置,既可放在主函数 main 之前,也可放 在 main 之后。 例如: 可把 max 函数置在 main 之后,也可以把它放在 main 之前。修改后的程序如下所示。 【例 8.1】
靳润昭C语言教程讲义 2001年2月17日 int max (int a, int b) f(a>b)return a else return b main int max(int a, int b) int printf( input two numbers: \n") scanf(%d%d",&x, &y) printf("maxmum=%d", z) 现在我们可以从函数定义、函数说明及函数调用的角度来分析整个程序,从中进一步了 解函数的各种特点。 程序的第1行至第5行为max函数定义。进入主函数后,因为准备调用max函数,故先 对max函数进行说明(程序第8行)。函数定义和函数说明并不是一回事,在后面还要专门讨 论。可以看出函数说明与函数定义中的函数头部分相同,但是末尾要加分号。程序第12行 为调用max函数,并把x,y中的值传送给max的形参a,b。max函数执行的结果(a或b) 将返回给变量z。最后由主函数输出z的值 83函数的参数和函数的值 83.1形式参数和实际参数 前面已经介绍过,函数的参数分为形参和实参两种。在本小节中,进一步介绍形参、实 参的特点和两者的关系。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数 则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参 的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实 现主调函数向被调函数的数据传送。 函数的形参和实参具有以下特点: 1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元 因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变 量 2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用 时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等 办法使实参获得确定值。 3.实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。 4.函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的 值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不 第4页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第4页 int max(int a,int b) { if(a>b)return a; else return b; } main() { int max(int a,int b); int x,y,z; printf("input two numbers:\n"); scanf("%d%d",&x,&y); z=max(x,y); printf("maxmum=%d",z); } 现在我们可以从函数定义、函数说明及函数调用的角度来分析整个程序,从中进一步了 解函数的各种特点。 程序的第 1 行至第 5 行为 max 函数定义。进入主函数后,因为准备调用 max 函数,故先 对 max 函数进行说明(程序第 8 行)。函数定义和函数说明并不是一回事,在后面还要专门讨 论。 可以看出函数说明与函数定义中的函数头部分相同,但是末尾要加分号。程序第 12 行 为调用 max 函数,并把 x, y 中的值传送给 max 的形参 a, b。max 函数执行的结果(a 或 b) 将返回给变量 z。最后由主函数输出 z 的值。 8.3 函数的参数和函数的值 8.3.1 形式参数和实际参数 前面已经介绍过,函数的参数分为形参和实参两种。在本小节中,进一步介绍形参、实 参的特点和两者的关系。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数 则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参 的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实 现主调函数向被调函数的数据传送。 函数的形参和实参具有以下特点: 1. 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。 因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变 量。 2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用 时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等 办法使实参获得确定值。 3. 实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。 4. 函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的 值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不
靳润昭C语言教程讲义 2001年2月17日 会变化 a|2b3 1015 【例8.2】可以说明这个问题。 main int n printf( input number \n") scanf(%d, &n) s(n) printf("n%d\n", n) It s(int n for(i=n-1: i>=l; i n-n printf("n=%d\n", n) 本程序中定义了一个函数s,该函数的功能是求∑n的值。在主函数中输入n值,并 作为实参,在调用时传送给s函数的形参量n(注意,本例的形参变量和实参变量的标识符 都为n,但这是两个不同的量,各自的作用域不同)。在主函数中用 printf语句输出一次n 值,这个n值是实参n的值。在函数s中也用 printf语句输出了一次n值,这个n值是形 参最后取得的n值0。从运行情况看,输入n值为100。即实参n的值为100。把此值传给 函数s时,形参n的初值也为100,在执行函数过程中,形参n的值变为5050。返回主函数 之后,输出实参n的值仍为100。可见实参的值不随形参的变化而变化。 832函数的返回值 函数的值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数 值。如调用正弦函数取得正弦值,调用例8.1的max函数取得的最大数等。对函数的值(或 称函数返回值)有以下一些说明: 1)函数的值只能通过 return语句返回主调函数。 return语句的一般形式为 return表达式 第5页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第5页 会变化。 【例 8.2】可以说明这个问题。 main() { int n; printf("input number\n"); scanf("%d",&n); s(n); printf("n=%d\n",n); } int s(int n) { int i; for(i=n-1;i>=1;i--) n=n+i; printf("n=%d\n",n); } 本程序中定义了一个函数 s,该函数的功能是求∑ni 的值。在主函数中输入 n 值,并 作为实参,在调用时传送给 s 函数的形参量 n( 注意,本例的形参变量和实参变量的标识符 都为 n,但这是两个不同的量,各自的作用域不同)。在主函数中用 printf 语句输出一次 n 值,这个 n 值是实参 n 的值。在函数 s 中也用 printf 语句输出了一次 n 值,这个 n 值是形 参最后取得的 n 值 0。从运行情况看,输入 n 值为 100。即实参 n 的值为 100。把此值传给 函数 s 时,形参 n 的初值也为 100,在执行函数过程中,形参 n 的值变为 5050。返回主函数 之后,输出实参 n 的值仍为 100。可见实参的值不随形参的变化而变化。 8.3.2 函数的返回值 函数的值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的 值。如调用正弦函数取得正弦值,调用例 8.1 的 max 函数取得的最大数等。对函数的值(或 称函数返回值)有以下一些说明: 1) 函数的值只能通过 return 语句返回主调函数。 return 语句的一般形式为: return 表达式;
靳润昭C语言教程讲义 2001年2月17日 或者为 return(表达式) 该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个 return 语句,但每次调用只能有一个 return语句被执行,因此只能返回一个函数值 函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类 型为准,自动进行类型转换 3)如函数值为整型,在函数定义时可以省去类型说明。 4)不返回函数值的函数,可以明确定义为“空类型”,类型说明符为“void"。如例 8.2中函数s并不向主函数返函数值,因此可定义为: void s(int n) 旦函数被定义为空类型后,就不能在主调函数中使用被调函数的函数值了 例如,在定义s为空类型后,在主函数中写下述语句 sum=s(n) 就是错误的 为了使程序有良好的可读性并减少出错,凡不要求返回值的函数都应定义为 空类型 84函数的调用 841函数调用的一般形式 前面己经说过,在程序中是通过对函数的调用来执行函数体的,其过程与其它语言的子 程序调用相似。 C语言中,函数调用的一般形式为 函数名(实际参数表) 对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数,变量或其它构造 类型数据及表达式。各实参之间用逗号分隔 842函数调用的方式 在C语言中,可以用以下几种方式调用函数 1.函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运 算。这种方式要求函数是有返回值的。例如:z=max(x,y)是一个赋值表达式,把max 的返回值赋予变量z。 2.函数语句:函数调用的一般形式加上分号即构成函数语句。例如: ("%d",a); scanf("%d",&b);都是以函数语句的方式调用函数 3.函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作 为实参进行传送,因此要求该函数必须是有返回值的。例如: printf("%d",max(x,y);即是把max调用的返回值又作为 printf函数的实参来使 用的。在函数调用中还应该注意的一个问题是求值顺序的问题。所谓求值顺序是指对实 第6页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第6页 或者为: return (表达式); 该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个 return 语句,但每次调用只能有一个 return 语句被执行,因此只能返回一个函数值。 2) 函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类 型为准,自动进行类型转换。 3) 如函数值为整型,在函数定义时可以省去类型说明。 4) 不返回函数值的函数,可以明确定义为“空类型”,类型说明符为“void”。如例 8.2 中函数 s 并不向主函数返函数值,因此可定义为: void s(int n) { …… } 一旦函数被定义为空类型后,就不能在主调函数中使用被调函数的函数值了。 例如,在定义 s 为空类型后,在主函数中写下述语句 sum=s(n); 就是错误的。 为了使程序有良好的可读性并减少出错, 凡不要求返回值的函数都应定义为 空类型。 8.4 函数的调用 8.4.1 函数调用的一般形式 前面已经说过,在程序中是通过对函数的调用来执行函数体的,其过程与其它语言的子 程序调用相似。 C语言中,函数调用的一般形式为: 函数名(实际参数表) 对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数,变量或其它构造 类型数据及表达式。各实参之间用逗号分隔。 8.4.2 函数调用的方式 在C语言中,可以用以下几种方式调用函数: 1. 函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运 算。这种方式要求函数是有返回值的。例如:z=max(x,y)是一个赋值表达式,把 max 的返回值赋予变量 z。 2. 函数语句:函数调用的一般形式加上分号即构成函数语句。例如: printf ("%d",a);scanf ("%d",&b);都是以函数语句的方式调用函数。 3. 函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作 为 实 参 进 行 传 送 , 因 此 要 求 该 函 数 必 须 是 有 返 回 值 的 。 例 如 : printf("%d",max(x,y)); 即是把 max 调用的返回值又作为 printf 函数的实参来使 用的。在函数调用中还应该注意的一个问题是求值顺序的问题。所谓求值顺序是指对实
靳润昭C语言教程讲义 2001年2月17日 参表中各量是自左至右使用呢,还是自右至左使用。对此,各系统的规定不一定相同 介绍 printf函数时已提到过,这里从函数调用的角度再强调一下 【例8.3】 main int i=8 printf("%d\n%d\n%dn%dn”,+i,一i,i++,i-) 如按照从右至左的顺序求值。运行结果应为 778 如对 printf语句中的++i,-i,i++,i一从左至右求值,结果应为 9889 应特别注意的是,无论是从左至右求值,还是自右至左求值,其输出顺序都是不变的, 即输出顺序总是和实参表中实参的顺序相同。由于 Turbo C现定是自右至左求值,所以结果 为8,7,7,8。上述问题如还不理解,上机一试就明白了 843被调用函数的声明和函数原型 在主调函数中调用某函数之前应对该被调函数进行说明(声明),这与使用变量之前要 先进行变量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函 数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。 其一般形式为: 类型说明符被调函数名(类型形参,类型形参.) 或为 类型说明符被调函数名(类型,类型) 括号内给出了形参的类型和形参名,或只给出形参类型。这便于编译系统进行检错,以 防止可能出现的错误 例8.1main函数中对max函数的说明为: int max(int a, int b) 或写为 int max(int, int) C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数说明。 1)如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调用 这时系统将自动对被调函数返回值按整型处理。例8.2的主函数中未对函数s作说 明而直接调用即属此种情形。 2)当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数 第7页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第7页 参表中各量是自左至右使用呢,还是自右至左使用。对此,各系统的规定不一定相同。 介绍 printf 函数时已提到过,这里从函数调用的角度再强调一下。 【例 8.3】 main() { int i=8; printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--); } 如按照从右至左的顺序求值。运行结果应为: 8 7 7 8 如对 printf 语句中的++i,--i,i++,i--从左至右求值,结果应为: 9 8 8 9 应特别注意的是,无论是从左至右求值, 还是自右至左求值,其输出顺序都是不变的, 即输出顺序总是和实参表中实参的顺序相同。由于 Turbo C 现定是自右至左求值,所以结果 为 8,7,7,8。上述问题如还不理解,上机一试就明白了。 8.4.3 被调用函数的声明和函数原型 在主调函数中调用某函数之前应对该被调函数进行说明(声明),这与使用变量之前要 先进行变量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函 数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。 其一般形式为: 类型说明符 被调函数名(类型 形参,类型 形参…); 或为: 类型说明符 被调函数名(类型,类型…); 括号内给出了形参的类型和形参名,或只给出形参类型。这便于编译系统进行检错,以 防止可能出现的错误。 例 8.1 main 函数中对 max 函数的说明为: int max(int a,int b); 或写为: int max(int,int); C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数说明。 1) 如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调用。 这时系统将自动对被调函数返回值按整型处理。例 8.2 的主函数中未对函数 s 作说 明而直接调用即属此种情形。 2) 当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数
靳润昭C语言教程讲义 2001年2月17日 再作说明而直接调用。例如例8.1中,函数max的定义放在main函数之前,因此 可在main函数中省去对max函数的函数说明 int max(inta,intb) 3)如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调 函数中,可不再对被调函数作说明。例如 char str(int a) float f(float b) maino char str(int a) float f(float b) 其中第一,二行对str函数和f函数预先作了说明。因此在以后各函数中无须对 str和f函数再作说明就可直接调用。 4)对库函数的调用不需要再作说明,但必须把该函数的头文件用 include命令包含在 源文件前部。 8.5函数的嵌套调用 C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下 级函数的问题。但是C语言允许在一个函数的定义中出现对另一个函数的调用。这样就出 现了函数的嵌套调用。即在被调函数中又调用其它函数。这与其它语言的子程序嵌套的情形 是类似的。其关系可表示如图。 main函数a函数b函数 调用a函数调用b函数 结束 图表示了两层嵌套的情形。其执行过程是:执行main函数中调用a函数的语句时,即 转去执行a函数,在a函数中调用b函数时,又转去执行b函数,b函数执行完毕返回a 函数的断点继续执行,a函数执行完毕返回main函数的断点继续执行。 第8页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第8页 再作说明而直接调用。例如例 8.1 中,函数 max 的定义放在 main 函数之前,因此 可在 main 函数中省去对 max 函数的函数说明 int max(int a,int b)。 3) 如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调 函数中,可不再对被调函数作说明。例如: char str(int a); float f(float b); main() { …… } char str(int a) { …… } float f(float b) { …… } 其中第一,二行对 str 函数和 f 函数预先作了说明。因此在以后各函数中无须对 str 和 f 函数再作说明就可直接调用。 4) 对库函数的调用不需要再作说明,但必须把该函数的头文件用 include 命令包含在 源文件前部。 8.5 函数的嵌套调用 C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下 一级函数的问题。但是C语言允许在一个函数的定义中出现对另一个函数的调用。这样就出 现了函数的嵌套调用。即在被调函数中又调用其它函数。这与其它语言的子程序嵌套的情形 是类似的。其关系可表示如图。 图表示了两层嵌套的情形。其执行过程是:执行 main 函数中调用 a 函数的语句时,即 转去执行 a 函数,在 a 函数中调用 b 函数时,又转去执行 b 函数,b 函数执行完毕返回 a 函数的断点继续执行,a 函数执行完毕返回 main 函数的断点继续执行
靳润昭C语言教程讲义 2001年2月17日 【例8.4】计算S=22!+32! 本题可编写两个函数,一个是用来计算平方值的函数f1,另一个是用来计算阶乘值的 函数f2。主函数先调f1计算出平方值,再在f1中以平方值为实参,调用f2计算其阶乘值, 然后返回f1,再返回主函数,在循环程序中计算累加和。 long fl(int p int k 2 (int) r=f2(k) return r long f2(int g) long c=l for(i=l; i<=g: i++) return c main int s=s+f1(i) printf ("ns=1d\n", s) 在程序中,函数f1和f2均为长整型,都在主函数之前定义,故不必再在主函数中对 f1和f2加以说明。在主程序中,执行循环程序依次把i值作为实参调用函数n1求i值。 在f1中又发生对函数f2的调用,这时是把1的值作为实参去调f2,在f2中完成求1 的计算。f2执行完毕把C值(即i!)返回给f1,再由们返回主函数实现累加。至此,由函 数的嵌套调用实现了题目的要求。由于数值很大,所以函数和一些变量的类型都说明为长整 否则会造成计算错误。 第9页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第9页 【例 8.4】计算 s=22 !+32 ! 本题可编写两个函数,一个是用来计算平方值的函数 f1,另一个是用来计算阶乘值的 函数 f2。主函数先调 f1 计算出平方值,再在 f1 中以平方值为实参,调用 f2 计算其阶乘值, 然后返回 f1,再返回主函数,在循环程序中计算累加和。 long f1(int p) { int k; long r; long f2(int); k=p*p; r=f2(k); return r; } long f2(int q) { long c=1; int i; for(i=1;i<=q;i++) c=c*i; return c; } main() { int i; long s=0; for (i=2;i<=3;i++) s=s+f1(i); printf("\ns=%ld\n",s); } 在程序中,函数 f1 和 f2 均为长整型,都在主函数之前定义,故不必再在主函数中对 f1 和 f2 加以说明。在主程序中,执行循环程序依次把 i 值作为实参调用函数 f1 求 i 2 值。 在 f1 中又发生对函数 f2 的调用,这时是把 i 2 的值作为实参去调 f2,在 f2 中完成求 i 2 ! 的计算。f2 执行完毕把 C 值(即 i 2 !)返回给 f1,再由 f1 返回主函数实现累加。至此,由函 数的嵌套调用实现了题目的要求。由于数值很大,所以函数和一些变量的类型都说明为长整 型,否则会造成计算错误
靳润昭C语言教程讲义 2001年2月17日 86函数的递归调用 个函数在它的函数体内调用它自身称为递归调用。这种函数称为递归函数。C语言允 许函数的递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自 身,每调用一次就进入新的一层 例如有函数f如下: 这个函数是一个递归函数。但是运行该函数将无休止地调用其自身,这当然是不正确的。 为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条 件判断,满足某种条件后就不再作递归调用,然后逐层返回。下面举例说明递归调用的执行 过程 【例8.5】用递归法计算n 用递归法计算n!可用下述公式表示 (n=0,1) 按公式可编程如下 long ff(int n) if(n<0) printf("n<0, input error") else f=ff(n-1)*n return(f) main int n printf( input a integer number: \n") scanf(%d", &n) ff(n) printf("%d!=%ld n, y) 程序中给出的函数ff是一个递归函数。主函数调用ff后即进入函数f执行,如果 n<0,n==0或n=1时都将结束函数的执行,否则就递归调用f函数自身。由于每次递归调用 的实参为n-1,即把n-1的值赋予形参n,最后当n1的值为1时再作递归调用,形参n的值 第10页
靳润昭 C 语言教程讲义 2001 年 2 月 17 日 第10页 8.6 函数的递归调用 一个函数在它的函数体内调用它自身称为递归调用。这种函数称为递归函数。C语言允 许函数的递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自 身,每调用一次就进入新的一层。 例如有函数 f 如下: int f(int x) { int y; z=f(y); return z; } 这个函数是一个递归函数。但是运行该函数将无休止地调用其自身,这当然是不正确的。 为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条 件判断,满足某种条件后就不再作递归调用,然后逐层返回。下面举例说明递归调用的执行 过程。 【例 8.5】用递归法计算 n! 用递归法计算 n!可用下述公式表示: n!=1 (n=0,1) n×(n-1)! (n>1) 按公式可编程如下: long ff(int n) { long f; if(n<0) printf("n<0,input error"); else if(n==0||n==1) f=1; else f=ff(n-1)*n; return(f); } main() { int n; long y; printf("\ninput a inteager number:\n"); scanf("%d",&n); y=ff(n); printf("%d!=%ld",n,y); } 程序中给出的函数 ff 是一个递归函数。主函数调用 ff 后即进入函数 ff 执行,如果 n<0,n==0 或 n=1 时都将结束函数的执行,否则就递归调用 ff 函数自身。由于每次递归调用 的实参为 n-1,即把 n-1 的值赋予形参 n,最后当 n-1 的值为 1 时再作递归调用,形参 n 的值