《C语言程序设计与数据结构》第6章 函数.ppt
第六章第六章 函数函数 C语言程序设计与数据结构总体要求:总体要求:掌握自定义函数的一般结构及定义函数的方法;掌握自定义函数的一般结构及定义函数的方法;掌握形参、实参、函数原型等重要概念;掌握形参、实参、函数原型等重要概念;掌握函数声明、函数调用的一般方法;掌握函数声明、函数调用的一般方法;了解局部变量、全局变量的概念;了解局部变量、全局变量的概念;掌握掌握auto型和型和static型局部变量的特点和用法。型局部变量的特点和用法。C语言程序设计与数据结构学习重点:学习重点:区分函数定义、函数说明的区别;区分函数定义、函数说明的区别;理解参数传递的意义。理解参数传递的意义。C语言程序设计与数据结构 在高级语言程序设计中往往将一个较大的程序分解在高级语言程序设计中往往将一个较大的程序分解成若干较小的、功能单一的程序模块来完成特定的成若干较小的、功能单一的程序模块来完成特定的功能,称其为功能,称其为子程序子程序。通过对这些子功能的组织和。通过对这些子功能的组织和调用,来实现整个程序的功能要求。调用,来实现整个程序的功能要求。C语言中,这语言中,这些功能比较独立的子程序成为些功能比较独立的子程序成为函数函数。函数是。函数是C语言语言程序的一种基本组成部分,程序的一种基本组成部分,C语言程序的功能是通语言程序的功能是通过函数之间的调用来实现的,一个完整的过函数之间的调用来实现的,一个完整的C语言程语言程序可由一个或多个函数组成,每个函数都有一个函序可由一个或多个函数组成,每个函数都有一个函数名。数名。C语言程序设计与数据结构为什么使用函数?为什么使用函数?第一,使用函数可以省去写重复代码。如果程序第一,使用函数可以省去写重复代码。如果程序中要多次使用某种特定的功能,那么只需编写一中要多次使用某种特定的功能,那么只需编写一个合适的函数即可。程序可以在任何需要的位置个合适的函数即可。程序可以在任何需要的位置调用该函数,并且同一个函数可以在不同的程序调用该函数,并且同一个函数可以在不同的程序中调用,就像在很多程序中使用中调用,就像在很多程序中使用printf()函数一()函数一样。样。第二,即使某种功能在程序中只使用一次,将其第二,即使某种功能在程序中只使用一次,将其以函数的形式来实现也是必要的,因为函数使得以函数的形式来实现也是必要的,因为函数使得程序更加模块化,从而有利于程序的阅读、修改程序更加模块化,从而有利于程序的阅读、修改和完善。和完善。C语言程序设计与数据结构C语言函数设计的一般原则为:语言函数设计的一般原则为:(1)界面清晰。函数的处理任务明确,函数之间数据界面清晰。函数的处理任务明确,函数之间数据传递越少越好。传递越少越好。(2)大小适中。函数太大,处理任务复杂,导致结构大小适中。函数太大,处理任务复杂,导致结构复杂,程序可读性差;反之,若函数太小,则程序复杂,程序可读性差;反之,若函数太小,则程序调用关系复杂,这样会降低程序的效率。调用关系复杂,这样会降低程序的效率。C语言程序设计与数据结构6.1函数分类在在C语语言言中中,从从用用户户使使用用的的角角度度看看,函函数数可可分分为为库库函数函数和和用户自定义函数用户自定义函数两种。两种。C语言提供了极为丰富的库函数,如前几章例题中语言提供了极为丰富的库函数,如前几章例题中反复用到的反复用到的scanf()、printf()、getchar()等函数均属于等函数均属于库函数。这类函数是由系统提供并定义好的,不必库函数。这类函数是由系统提供并定义好的,不必用户再去定义,用户只需掌握函数的功能,并学会用户再去定义,用户只需掌握函数的功能,并学会正确调用这些函数即可,至于函数内部是如何实现正确调用这些函数即可,至于函数内部是如何实现这些功能的,用户不必知道。但是,仅有这些功能的,用户不必知道。但是,仅有C语言的语言的库函数在实际编程中往往是不够用的,这就需要用库函数在实际编程中往往是不够用的,这就需要用户按需要定义和编写自己的函数来解决某些问题。户按需要定义和编写自己的函数来解决某些问题。C语言程序设计与数据结构事实上,一个具备一定规模事实上,一个具备一定规模C程序往往由多个函数程序往往由多个函数组成。其中必有一个名为组成。其中必有一个名为main的主函数,由的主函数,由main来来调用其他函数。必要的话,其他函数还可以调用另调用其他函数。必要的话,其他函数还可以调用另外的函数。同一个函数可以被多个函数调用一次或外的函数。同一个函数可以被多个函数调用一次或者多次。者多次。从总的框架来看,是将一个大的计算任务划分为一从总的框架来看,是将一个大的计算任务划分为一个一个小的任务,这些小的任务均由函数来完成。个一个小的任务,这些小的任务均由函数来完成。而函数既可以是而函数既可以是C的标准库函数,也可以是用户自的标准库函数,也可以是用户自定义函数。定义函数。注意:函数必须先定义,后调用。注意:函数必须先定义,后调用。C语言程序设计与数据结构6.1.1函数分类从从函函数数定定义义的的角角度度看看,函函数数可可分分为为库库函函数数和和用用户户定义函数两种定义函数两种 语语言言的的函函数数兼兼有有其其它它语语言言中中的的函函数数和和过过程程两两种种功功能能,从从这这个个角角度度看看,又又可可把把函函数数分分为为有有返返回回值值函数和无返回值函数两种。函数和无返回值函数两种。从从主主调调函函数数和和被被调调函函数数之之间间数数据据传传送送的的角角度度看看又又可分为无参函数和有参函数两种。可分为无参函数和有参函数两种。语言提供了极为丰富的库函数语言提供了极为丰富的库函数C语言程序设计与数据结构 还应该指出的是,在语言中,所有的函数定还应该指出的是,在语言中,所有的函数定义,包括主函数义,包括主函数main在内,都是平行的。在内,都是平行的。也就是说,在一个函数的函数体内,不能再定也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。但是函数之间允义另一个函数,即不能嵌套定义。但是函数之间允许相互调用,也允许嵌套调用。习惯上把调用者称许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。为主调函数。函数还可以自己调用自己,称为递归函数还可以自己调用自己,称为递归调用。调用。main 函数是主函数,它可以调用其它函数,函数是主函数,它可以调用其它函数,而不允许被其它函数调用。而不允许被其它函数调用。因此,程序的执行总是从因此,程序的执行总是从main函数开始,完函数开始,完成对其它函数的调用后再返回到成对其它函数的调用后再返回到main函数,最后由函数,最后由main函数结束整个程序。一个源程序必须有,也函数结束整个程序。一个源程序必须有,也只能有一个主函数只能有一个主函数main。C语言程序设计与数据结构6.1.2 文件包含文件包含是文件包含是C预处理程序的一项重要功能。预处理程序的一项重要功能。文件包含命令行的一般形式为:文件包含命令行的一般形式为:#include#include 文件名文件名 在前面我们已多次用此命令包含过库函数的头文件。例如:在前面我们已多次用此命令包含过库函数的头文件。例如:#include#include stdiostdio.h.h文件包含命令的功能是把指定的文件插入该命令行位置取代该文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文命令行,从而把指定的文件和当前的源程序文件连成一个源文件。被读入的源文件必须用双引号或尖括号括起来。件。被读入的源文件必须用双引号或尖括号括起来。在程序设计中,文件包含是很有用的。一个大的程序可以分为在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都去书写那含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。些公用量,从而节省时间,并减少出错。C语言程序设计与数据结构对文件包含命令还要说明以下几点:1.包含命令中的文件名可以用双引号括起来,也可以用尖括号包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来。例如以下写法都是允许的:括起来。例如以下写法都是允许的:#include stdio.h#include 但是这两种形式是有区别的:使用尖括号表示在包含文件目但是这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找录中去查找(包含目录是由用户在设置环境时设置的包含目录是由用户在设置环境时设置的),而不,而不在源文件目录去查找;在源文件目录去查找;使用双引号则表示首先在当前的源文件目录中查找,若未找使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。用户编程时可根据自己文件所在到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。的目录来选择某一种命令形式。2.一个一个include命令只能指定一个被包含文件,若有多个文件要命令只能指定一个被包含文件,若有多个文件要包含,则需用多个包含,则需用多个include命令。命令。3.文件包含允许嵌套,即在一个被包含的文件中又可以包含另文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件一个文件 C语言程序设计与数据结构6.2函数的定义函数的定义6.2.1函数定义的一般形式函数定义的一般形式1任何函数(包括主函数任何函数(包括主函数main())都是由函数说明和函数体两部分组成。)都是由函数说明和函数体两部分组成。根据函数是否需要参数,可将函数分为无参函数和有参函数两种。根据函数是否需要参数,可将函数分为无参函数和有参函数两种。函数返回值类型函数返回值类型 函数名函数名(形式参数表形式参数表)函数体函数体 定义说明:定义说明:函数名:是符合标识符命名规则的任何合法标识符。函数名:是符合标识符命名规则的任何合法标识符。形参表是用逗号分隔的一组变量说明,其格式如下:形参表是用逗号分隔的一组变量说明,其格式如下:数据类型数据类型1形参形参1,数据类型数据类型2形参形参2,数据类型数据类型n 形参形参n函数体是实现函数功能的全部语句。函数体是实现函数功能的全部语句。函数类型有两种情况:当函数有具体的返回值时,函数类型为函数值的数函数类型有两种情况:当函数有具体的返回值时,函数类型为函数值的数据类型;当函数只是完成特定的操作,而没有具体的返回值时,函数类据类型;当函数只是完成特定的操作,而没有具体的返回值时,函数类型为型为void型。函数的类型可以省略,当不指明函数类型时,系统默认为型。函数的类型可以省略,当不指明函数类型时,系统默认为int型。型。C语言程序设计与数据结构例例 6.1 定义一个函数,用于求两个数中的大数。定义一个函数,用于求两个数中的大数。1#include 2 int max(int n1,int n2)/*定义一个函数定义一个函数max()*/3 4 return(n1n2?n1:n2);5 6 void main()7 8 int max(int n1,int n2);/*函数说明函数说明*/9 int num1,num2;10 printf(input two numbers:n);11 scanf(%d%d,&num1,&num2);12 printf(max=%dn,max(num1,num2);13 C语言程序设计与数据结构第第8,9行为说明语句部分,用来说明(定义、申明)函数和变行为说明语句部分,用来说明(定义、申明)函数和变量。第量。第10-12行为执行语句部分。行为执行语句部分。2说明说明(1)函数不允许嵌套定义。)函数不允许嵌套定义。在语言中,所有函数(包括主函数在语言中,所有函数(包括主函数main())都是平行的。一)都是平行的。一个函数的定义,可以放在程序中的任意位置,即放在主函数个函数的定义,可以放在程序中的任意位置,即放在主函数main()之前或之后都可以。但在一个函数的函数体内,不能再定之前或之后都可以。但在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。义另一个函数,即不能嵌套定义。(2)空函数)空函数既无参数、函数体又为空的函数。其一般形式既无参数、函数体又为空的函数。其一般形式为:为:函数类型函数类型 函数名(函数名(void)C语言程序设计与数据结构6.2.2形参和实参形参和实参函数的参数分为函数的参数分为 形参形参 和和 实参实参 两种,作用是实现数两种,作用是实现数据传送。据传送。形参出现在函数定义中,只能在该函数体内使用。形参出现在函数定义中,只能在该函数体内使用。发生函数调用时,调用函数把实参的值复制发生函数调用时,调用函数把实参的值复制1份,份,传送给被调用函数的形参,从而实现调用函数向被传送给被调用函数的形参,从而实现调用函数向被调用函数的数据传送。调用函数的数据传送。C语言程序设计与数据结构例例 6.2 实参对形参的数据传递实参对形参的数据传递(无返回值无返回值)。1#include 2 void main()3 4 void s(int n);/*说明函数说明函数*/5 int n=100;/*定义实参定义实参n,并初始化,并初始化*/6 s(n);/*调用函数调用函数*/7 printf(n_s=%dn,n);/*输出调用后实参的值,便于进行比较输出调用后实参的值,便于进行比较*/8 9 void s(int m)10 11 int i;12 printf(m=%dn,m);/*输出改变前形参的值输出改变前形参的值*/13 for(i=m-1;i=1;i-)14 m=m+i;/*改变形参的值改变形参的值*/15 printf(m=%dn,m);/*输出改变后形参的值输出改变后形参的值*/16 程序运行结果为:m=100 m=5050n_s=100 C语言程序设计与数据结构例例6.3 实参对形参的数据传递实参对形参的数据传递(有返回值有返回值)。1#include 2 void main()3 4 int max(int x,int y);5 int a,b,c;6 scanf(%d,%d,&a,&b);7 c=max(a,b);8 printf(Max is%dn,c);9 10 int max(int x,int y)11 12 int z;13 z=xy?x:y;14 return z;15 运行情况如下:输入:5,7 输出:max is 7 C语言程序设计与数据结构说明:说明:(1)实参可以是常量、变量、表达式、函数等。无论实参)实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此,应预先用赋值、输的值,以便把这些值传送给形参。因此,应预先用赋值、输入等办法,使实参获得确定的值。入等办法,使实参获得确定的值。(2)形参变量只有在被调用时,才分配内存单元,调用结)形参变量只有在被调用时,才分配内存单元,调用结束后,即刻释放所分配的内存单元。因此,形参只有在该函束后,即刻释放所分配的内存单元。因此,形参只有在该函数内有效。调用结束后,则不能再使用该形参变量。数内有效。调用结束后,则不能再使用该形参变量。(3)实参对形参的数据传送是单向的,即只能把实参的值)实参对形参的数据传送是单向的,即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。传送给形参,而不能把形参的值反向地传送给实参。(4)实参和形参占用不同的内存单元,即使同名也互不影)实参和形参占用不同的内存单元,即使同名也互不影响。响。(5)实参与形参的类型应当相同或赋值兼容。)实参与形参的类型应当相同或赋值兼容。C语言程序设计与数据结构6.2.3函数的返回值函数的返回值语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。又可把函数分为有返回值函数和无返回值函数两种。1函数返回值与函数返回值与return语句语句 函数的返回值,是通过函数中的函数的返回值,是通过函数中的return语句来获得的。语句来获得的。(1)return语句的一般格式:语句的一般格式:return(返回值表达式返回值表达式);(2)return语句的功能:返回调用函数,并将语句的功能:返回调用函数,并将“返回值表达式返回值表达式”的值带的值带给调用函数。给调用函数。注意:被调用函数中无注意:被调用函数中无return语句,并不是不返回一个值,而是一个不语句,并不是不返回一个值,而是一个不确定的值。为了明确表示不返回值,可以将函数类型定义为确定的值。为了明确表示不返回值,可以将函数类型定义为“void”,表示为表示为“无(空)类型无(空)类型”。2函数类型函数类型 在定义函数时,对函数类型的说明,应与在定义函数时,对函数类型的说明,应与return语句中返回值表达式的语句中返回值表达式的类型一致,也就是说函数的类型是函数返回值的类型,它可以是我们已类型一致,也就是说函数的类型是函数返回值的类型,它可以是我们已经学习过的经学习过的int、char、float、double中的任意类型,也可以是我们要在后中的任意类型,也可以是我们要在后面学习的构造数据类型和指针类型。如果不一致,则以函数类型为准。面学习的构造数据类型和指针类型。如果不一致,则以函数类型为准。如果缺省函数类型,则系统一律按整型处理。如果缺省函数类型,则系统一律按整型处理。C语言程序设计与数据结构例例6.4 求参数求参数n的平方的平方 double sqare(double n)return n*n;下面这个函数用来判断参数下面这个函数用来判断参数n是否为正数是否为正数 int isPositive(int n)if(n0)return 1;else return 0;注意函数注意函数isPositive,虽然没有错,但显得很笨拙,更好的方法是:,虽然没有错,但显得很笨拙,更好的方法是:int isPositive(int n)return n0;或或 int isPositive(int n)return(n0)?1:0 C语言程序设计与数据结构再举一个不带参数没有返回值的函数的例子再举一个不带参数没有返回值的函数的例子:void hello()printf(“Hi!n”);良好的程序设计习惯:良好的程序设计习惯:为了使程序具有良好的可读性并减少出错,凡不要为了使程序具有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型,即使函数类型求返回值的函数都应定义为空类型,即使函数类型为整型,也不使用系统的缺省处理。为整型,也不使用系统的缺省处理。C语言程序设计与数据结构6.3 函数的声明和调用 6.3 函数的声明和调用函数的声明和调用 编译系统在处理调用时,必须从程序中获得完成函数调用所编译系统在处理调用时,必须从程序中获得完成函数调用所必需的接口信息,用以确认函数调用在语法及语义上的正确必需的接口信息,用以确认函数调用在语法及语义上的正确性,用以判断是否有传递的参数或返回值的数据类型,从而性,用以判断是否有传递的参数或返回值的数据类型,从而生成正确的函数调用代码。提供接口信息任务一般由函数原生成正确的函数调用代码。提供接口信息任务一般由函数原型来担任。函数原型的基本格式如下:型来担任。函数原型的基本格式如下:函数类型函数类型 函数名函数名(数据类型(数据类型 形参形参【,数据类型,数据类型2 形参形参2】););函数原型的格式就是在函数定义格式的基础上去掉了函数体,函数原型的格式就是在函数定义格式的基础上去掉了函数体,再加上分号构成的。函数调用的接口信息必须提前提供,因再加上分号构成的。函数调用的接口信息必须提前提供,因此函数原型必须位于对该函数的第一次调用处之前。此函数原型必须位于对该函数的第一次调用处之前。C语言程序设计与数据结构语言同时又规定,在以下语言同时又规定,在以下2种情况下,可以省去对种情况下,可以省去对被调用函数的说明:被调用函数的说明:(1)当被调用函数的函数定义出现在调用函数之)当被调用函数的函数定义出现在调用函数之前时。因为在调用之前,编译系统已经知道了被调前时。因为在调用之前,编译系统已经知道了被调用函数的函数类型、参数个数、类型和顺序。可见用函数的函数类型、参数个数、类型和顺序。可见函数定义也兼有提供接口信息的功能。函数定义也兼有提供接口信息的功能。(2)如果在所有函数定义之前,在函数外部(例)如果在所有函数定义之前,在函数外部(例如文件开始处)预先对各个函数进行了说明,则在如文件开始处)预先对各个函数进行了说明,则在调用函数中可缺省对被调用函数的说明。调用函数中可缺省对被调用函数的说明。C语言程序设计与数据结构6.3.2 函数的调用 在程序中,是通过对函数的调用来执行函数体的,在程序中,是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似。其过程与其它语言的子程序调用相似。C语言中,函数调用的一般形式为:语言中,函数调用的一般形式为:函数名(函数名(【实参表实参表】););切记切记:实际参数的个数、类型和顺序,应该与被调用函数实际参数的个数、类型和顺序,应该与被调用函数所要求的参数个数、类型和顺序一致,才能正确地所要求的参数个数、类型和顺序一致,才能正确地进行数据传递。进行数据传递。C语言程序设计与数据结构说明:说明:(1)调用函数时,函数名称必须与具有该功能的自)调用函数时,函数名称必须与具有该功能的自定义函数名称完全一致。定义函数名称完全一致。(2)实参在类型上按顺序与形参必须一一对应和匹)实参在类型上按顺序与形参必须一一对应和匹配。如果类型不匹配,配。如果类型不匹配,C编译程序将按赋值兼容的编译程序将按赋值兼容的规则进行转换。如果实参和形参的类型不相互兼容,规则进行转换。如果实参和形参的类型不相互兼容,通常并不给出出错信息,且程序仍然继续执行,只通常并不给出出错信息,且程序仍然继续执行,只是得不到正确的结果。是得不到正确的结果。(3)如果实参表中包括多个参数,对实参的求值顺)如果实参表中包括多个参数,对实参的求值顺序随系统而异。有的系统按自左向右顺序求实参的序随系统而异。有的系统按自左向右顺序求实参的值,有的系统则相反。值,有的系统则相反。Turbo C和和VC+是按自右向左是按自右向左的顺序进行的。的顺序进行的。C语言程序设计与数据结构6.4函数的嵌套与递归函数的嵌套与递归 C语言中的函数定义是相互独立的,函数和函数语言中的函数定义是相互独立的,函数和函数之间没有从属关系,即一个函数内不允许包含另一之间没有从属关系,即一个函数内不允许包含另一个函数的定义。一个函数既可以被其他的函数调用,个函数的定义。一个函数既可以被其他的函数调用,同时,它也可以调用别的函数,这就是函数的嵌套同时,它也可以调用别的函数,这就是函数的嵌套调用。函数的嵌套调用为自顶向下、逐步求精及模调用。函数的嵌套调用为自顶向下、逐步求精及模块化的结构化程序设计技术提供了最基本的支持。块化的结构化程序设计技术提供了最基本的支持。C语言程序设计与数据结构函数的嵌套调用关系可以用图函数的嵌套调用关系可以用图6-1表示。这是一个两表示。这是一个两层嵌套(连同层嵌套(连同main主函数共三层)调用的示意图。主函数共三层)调用的示意图。其执行过程是:其执行过程是:main函数调用a的函数结束a函数调用b函数a函数结束b函数b函数结束C语言程序设计与数据结构6.4.2函数的递归调用函数的递归调用 所谓递归调用,就是在调用一个函数过程中又出现直接或间接地调用该所谓递归调用,就是在调用一个函数过程中又出现直接或间接地调用该函数本身。当一个问题具有递归关系时,采用递归调用方式,可以使程函数本身。当一个问题具有递归关系时,采用递归调用方式,可以使程序更加简洁。序更加简洁。1、直接递归调用、直接递归调用一个函数可直接调用该函数本身的情况称为直接递归调用。例如:一个函数可直接调用该函数本身的情况称为直接递归调用。例如:float func(int n)int m;float f;f=func(m);C语言程序设计与数据结构间接递归调用间接递归调用一个函数可间接的调用该函数本身的情况称为间一个函数可间接的调用该函数本身的情况称为间接递归调用。例如:接递归调用。例如:func2(int x)int y;func1(y);func2(int x)int y;func1(y);C语言程序设计与数据结构例例6.5从键盘输入一非负整数从键盘输入一非负整数n,并求出,并求出n!的值。由于的值。由于n!=n*(n-1)!,所以,此题目可以利用函数的递归调用来,所以,此题目可以利用函数的递归调用来实现。实现。程序如下:程序如下:#include stdio.hlong fact(int n)int f;if(n=0)f=1;else f=n*fact(n-1);return(f);void main()int n;long int result;while(1)printf(Input a number:);scanf(%d,&n);if(n0)break;result=fact(n);printf(Result=%dn,result);C语言程序设计与数据结构说明:说明:(1)main函数中的函数中的while(1)无限循环,是为了读取正整数而设置的,当)无限循环,是为了读取正整数而设置的,当读入的数是零或负整数时,重复读操作,直到读入的数大于读入的数是零或负整数时,重复读操作,直到读入的数大于0为止。为止。(2)在递归调用过程中,必须保证一步比一步更加简单,最后是有终结)在递归调用过程中,必须保证一步比一步更加简单,最后是有终结的,而不能无限递归下去。在本例中,当的,而不能无限递归下去。在本例中,当n=0时时f=1,它不再递归下去,它不再递归下去,因而结束递归调用的过程。因而结束递归调用的过程。下面给出当下面给出当n=4时,时,fact函数的简单递归调用过程:函数的简单递归调用过程:fact(4)=4*fact(3)=4*3*fact(2)=4*3*2*fact(1)=4*3*2*1*fact(0)=4*3*2*1*1 =24函数递归调用是函数递归调用是C语言的重要特点之一。尽管函数的递归调用并不节省存语言的重要特点之一。尽管函数的递归调用并不节省存储空间,运行效率也不高,但当有一个问题有递归关系时,采用递归调储空间,运行效率也不高,但当有一个问题有递归关系时,采用递归调用处理方法,将使得所要处理的问题更易于表达,程序也更加简洁。另用处理方法,将使得所要处理的问题更易于表达,程序也更加简洁。另外,任何有意义的递归调用总是由两部分组成,即递归方式和递归终止外,任何有意义的递归调用总是由两部分组成,即递归方式和递归终止条件。条件。C语言程序设计与数据结构6.5 局部变量与全局变量局部变量与全局变量下列变量是局部变量:下列变量是局部变量:1在一个函数内部定义的变量在一个函数内部定义的变量2函数的形式参数函数的形式参数3在某个复合语句中定义的变量在某个复合语句中定义的变量说明:说明:1局部变量只在自己的范围内有效局部变量只在自己的范围内有效2如果局部变量的有效范围有重叠,则有效范围小如果局部变量的有效范围有重叠,则有效范围小的优先的优先C语言程序设计与数据结构关于局部变量的作用域明确以下几点:关于局部变量的作用域明确以下几点:1主函数主函数main()中定义的内部变量,也只能在主函中定义的内部变量,也只能在主函数中使用,其它函数不能使用。同时,主函数中也数中使用,其它函数不能使用。同时,主函数中也不能使用其它函数中定义的内部变量。不能使用其它函数中定义的内部变量。2形参变量是局部变量,属于被调用函数;实参形参变量是局部变量,属于被调用函数;实参变量,则是调用函数的局部变量。变量,则是调用函数的局部变量。3允许在不同的函数中使用相同的变量名,它们允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。不会发生混淆。4在复合语句中也可定义变量,其作用域只在复在复合语句中也可定义变量,其作用域只在复合语句范围内。合语句范围内。C语言程序设计与数据结构例例6.6 实现整形数的加运算:实现整形数的加运算:#include int sum(int a,int b)a=a+b;b=a+b;return a;void main()int a=1,b=3,c;c=sum(a,b);printf(Sum of%d,%d is%d n,a,b,c);C语言程序设计与数据结构6.5.2 全局变量全局变量在函数之外定义的变量(外部变量)是全局变量。在函数之外定义的变量(外部变量)是全局变量。全局变量的有效范围是:从定义变量的位置开始到源全局变量的有效范围是:从定义变量的位置开始到源文件结束。在一个函数中,既可以使用本函数中的文件结束。在一个函数中,既可以使用本函数中的局部变量,也可使用有效的全局变量。局部变量,也可使用有效的全局变量。说明:说明:1.利用全局变量可以在函数间传递数据。利用全局变量可以在函数间传递数据。2.应尽量少使用全局变量应尽量少使用全局变量3.如果全局变量与局部变量同名,则局部变量优先。如果全局变量与局部变量同名,则局部变量优先。4.若全局变量的定义位于引用该变量的函数之后,用若全局变量的定义位于引用该变量的函数之后,用extern 语句说明为外部变量。语句说明为外部变量。C语言程序设计与数据结构6.6 内部函数与外部函数内部函数与外部函数6.6.1 内部函数内部函数内部函数只能被所在源文件中的其他函数调用。如果在一个内部函数只能被所在源文件中的其他函数调用。如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序中其它文件的函数调用,这种函数称为内部函数。被同一程序中其它文件的函数调用,这种函数称为内部函数。定义一个内部函数,只需在函数类型前再加一个定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,关键字即可,内部函数用内部函数用static说明说明:由于内部函数使用了关键字由于内部函数使用了关键字“static”,所以内部函数又称,所以内部函数又称静态函数。但此处静态函数。但此处“static”的含义不是指存储方式,而是的含义不是指存储方式,而是指将函数的作用域仅局限于本文件。指将函数的作用域仅局限于本文件。使用内部函数的好处是:不同的人编写不同的函数时,不用使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。为同名也没有关系。C语言程序设计与数据结构6.6.2 外部函数外部函数外部函数不仅能被所在源文件中的函数调用,而且能被其他外部函数不仅能被所在源文件中的函数调用,而且能被其他源文件中的函数调用外部函数用源文件中的函数调用外部函数用extern说明,也可以省略说明,也可以省略extern在需要调用此函数的文件中,用在需要调用此函数的文件中,用extern声明。声明。多个源程序文件的编译和连接多个源程序文件的编译和连接通过前面的学习我们已经知道开发一个通过前面的学习我们已经知道开发一个C程序必须经过编辑、程序必须经过编辑、编译、连接等步骤。同时我们还应当知道,当开发的编译、连接等步骤。同时我们还应当知道,当开发的C程序程序较大(较大(TC2.0环境下环境下C源程序文件长度大于源程序文件长度大于1024KB)或有几个)或有几个人参与开发时,就不可能将源程序写在一个文件中。在多数人参与开发时,就不可能将源程序写在一个文件中。在多数情况下情况下C语言要求将函数声明编成头文件而单独存在,一般语言要求将函数声明编成头文件而单独存在,一般不应放在源文件中,这就产生了一个程序由多个源文件组成不应放在源文件中,这就产生了一个程序由多个源文件组成的问题。的问题。C语言程序设计与数据结构