靳润昭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 表达式;