1、第第8章章函函数数第1页第1页8.1 概述C语语言言是是通通过过函函数数来来实实现现模模块块化化程程序序设设计计。一一个个较较大大程程序序应应分分为为若若干干程程序序模模块块,每每个个模模块块实实现现一一个个特特定定功功效效,这这个个模模块块称称为为子子程程序序。C子子程程序序是通过是通过函数函数实现,函数是实现,函数是C语言程序语言程序基本单位基本单位。第2页第2页函数构成源程序是由函数构成。函数是源程序基本模源程序是由函数构成。函数是源程序基本模块,通过对函数模块调用实现特定功块,通过对函数模块调用实现特定功能。能。实用程序往往包括一实用程序往往包括一个主函数个主函数main()和和若干其
2、它若干其它函数函数。其中主函数。其中主函数main()是是必须必须,它是所有函数,它是所有函数执行起点执行起点。由。由主函数调用其它函数主函数调用其它函数,其它函数也,其它函数也能够能够互相调用互相调用,同一函数能够被,同一函数能够被一个或多个一个或多个函数函数调用任意多次。调用示意图下列:调用任意多次。调用示意图下列:第3页第3页 程序所有工作都是由各式各样函数完毕,因此也把语言称为函数式语言。f11()f11()f31()f1()f11();f2()f11();f22();main()f1();f2();第4页第4页程序开发中使用函数长处1 1)使用函数能)使用函数能够够控制控制任任务规务
3、规模模2 2)使用函数能)使用函数能够够控制控制变变量作用范量作用范围围3 3)使用函数,程序开发能够由)使用函数,程序开发能够由多人分工协作多人分工协作 4 4)使用函数,能够)使用函数,能够重新利用重新利用已有、调式好、已有、调式好、成熟程序模块成熟程序模块 5)函数模块函数模块相对独立,功效单一相对独立,功效单一,可混合编写也,可混合编写也可独立编写调试。可独立编写调试。第5页第5页函数一些阐明1)一个)一个C程序由多个程序模块构成,每个模块作为一个程序由多个程序模块构成,每个模块作为一个源源程序文献,程序文献,多个源程序文献构成一个多个源程序文献构成一个C程序,这样程序,这样便于便于分
4、分别编写分别编译,提升调试效率,一个源程序文献可为多别编写分别编译,提升调试效率,一个源程序文献可为多个个C程序程序共用共用。2)一个源程序文献由)一个源程序文献由一个或多个函数一个或多个函数及其相关内容(如数及其相关内容(如数据定义等)构成,一个源程序文献是一个据定义等)构成,一个源程序文献是一个基本编译单位。基本编译单位。3)C程序执行从主函数程序执行从主函数main()开始(称为()开始(称为主调函数主调函数),),能够调用其它函数(称为能够调用其它函数(称为被调用函数被调用函数),调用流程),调用流程返回返回main(),最后函数在(),最后函数在main()中()中结束结束。4)所有
5、函数都是)所有函数都是平行平行,在定义时候是,在定义时候是分别进行分别进行,互相,互相独立独立,无从属无从属关系,关系,不可嵌套不可嵌套定义。函数间可互相调用,但定义。函数间可互相调用,但不能不能调用主函数调用主函数,主函数只能由系统调用。,主函数只能由系统调用。第6页第6页函数分类1.从用户使用角度 1)标准函数(库函数),由系统提供,用户无须自定义可直接使用,注意:不同C编译系统提供库函数可能有些不同 2)用户自定义函数,用来处理用户专门需要。2.从函数形式 1)无参函数。在调用函数时,main不向被调用函数传递数据,只用来执行一组操作。2)有参函数,主调函数在调用被调用函数时,经过参数向
6、其传递数据,普通情况下,执行被调用函数时,得到一个函数值,供主调函数使用。第7页第7页8.2 函数定义普通形式函数应当先定义,后调用函数应当先定义,后调用(1)无参函数普通形式无参函数普通形式函数类型函数类型函数名函数名()阐明语句部分;阐明语句部分;可执行语句部分;可执行语句部分;无参函数普通不需要返回函数值,函数类型无参函数普通不需要返回函数值,函数类型void类型类型(空类型)(空类型)第8页第8页2)有参函数普通形式)有参函数普通形式函数类型函数类型函数名函数名(形参表列形参表列)阐明语句部分;阐明语句部分;可执行语句部分;可执行语句部分;例:例:intmax(x,y)intx,y;/
7、形式参数阐明形式参数阐明/intz;/函数体中阐明部分函数体中阐明部分/z=xy?x:y;return(z);这两行能够写成一行:这两行能够写成一行:intmax(intx,inty)第9页第9页3“空函数空函数”类型阐明符类型阐明符函数名函数名()“空函数空函数”什么操作也不做什么操作也不做。其作用是在此处。其作用是在此处留留一函数位置一函数位置,以便未来扩充功效之用。函数名也,以便未来扩充功效之用。函数名也在未来换取实际函数名。在未来换取实际函数名。第10页第10页函数定义一些阐明1.函函数数头头(首首部部):阐阐明明了了函函数数类类型型、函函数数名名称称及及参数。参数。(1)函函数数类类
8、型型:函函数数返返回回值值数数据据类类型型,能能够够是是基基本本数数据据类类型型也也能能够够是是结结构构类类型型。假假如如省省略略默默认认为为int,假如不返回值,定义为,假如不返回值,定义为void类型。类型。(2)函函数数名名:给给函函数数取取名名字字,以以后后用用这这个个名名字字调调用。函数名由用户命名,命名规则同标识符。用。函数名由用户命名,命名规则同标识符。(3)(3)函数名后面是参数表,无参函数没有参数传函数名后面是参数表,无参函数没有参数传递,但递,但“()()”号不能省略,这是格式要求。号不能省略,这是格式要求。参参数表阐明参数类型和形式参数名称,各个形式参数表阐明参数类型和形
9、式参数名称,各个形式参数用数用“,”分隔。分隔。第11页第11页2.函函数数体体:函函数数首首部部下下用用一一对对括括起起来来部部分分。假假如如函函数数体体内内有有多多个个,最最外外层层是是函函数数体体范围。范围。函函数数体体普普通通包包括括申申明明部部分分、执执行行部部分分两两部部分。分。1)申申明明部部分分:在在这这部部分分定定义义本本函函数数所所使使用用变变量和进行相关申明(如函数申明)。量和进行相关申明(如函数申明)。2)执执行行部部分分:程程序序段段,由由若若干干条条语语句句构构成成(能够在其中调用其它函数)。(能够在其中调用其它函数)。第12页第12页例:输入三个整数,求三个整数中
10、最大值例:输入三个整数,求三个整数中最大值不使用函数不使用函数(除除mainmain外外)main()main()int n1,n2,n3,nmax;int n1,n2,n3,nmax;scanf(“%d%d%d”,&n1,&n2,&n3);scanf(“%d%d%d”,&n1,&n2,&n3);if(n1n2)if(n1n2)nmax=n1;nmax=n1;else nmax=n2;else nmax=n2;if(n3max)if(n3max)max=n3;max=n3;printf(“max=%dn”printf(“max=%dn”,nmax);nmax);第13页第13页使用函数使用函数
11、intmax(int,int,int);/*函数申明函数申明*/main()intn1,n2,n3,nmax;scanf(“%d%d%d”,&n1,&n2,&n3);nmax=max(n1,n2,n3);printf(“max=%dn“,nmax);intmax(intx,inty,intz)intm;if(xy)m=x;elsem=y;if(zm)m=z;returnm;像像调调用用库库函函数数同样调用同样调用函数定义函数定义第14页第14页8.3函数参数和函数值函数参数和函数值8.3.1形式参数与实际参数形式参数与实际参数在调用函数时,大多情况下,主调与被调函在调用函数时,大多情况下,主调
12、与被调函数间有数间有数据传递关系数据传递关系,这就是有参函数。在定义,这就是有参函数。在定义函数时,函数名后面括号中变量名称为函数时,函数名后面括号中变量名称为“形形式式参参数数”,在主调函数中调用一个函数时,函数名后,在主调函数中调用一个函数时,函数名后面括号中参数(能够是表示式)称为面括号中参数(能够是表示式)称为“实实际际参参数数”。发生函数调用时,调用函数把发生函数调用时,调用函数把实参值实参值复制一复制一份,份,传送给传送给被调用函数被调用函数形参形参,从而实现调用函数,从而实现调用函数向被调用函数向被调用函数数据传送数据传送。第15页第15页例例从键盘输入两个数,输出其中较大一个。
13、从键盘输入两个数,输出其中较大一个。main()inta,b,c;scanf(“%d,%d”,&a,&b);c=max(a,b);/a,b为实际参数为实际参数/printf(“Maxis%d”,c);max(x,y)/x,y为形式参数为形式参数/intx,y;intz;z=xy?x:y;return(z);第16页第16页说明:1)形参变量在被调用前不占用存放单元;在被调用结束 后,形参所占存放单元亦被释放。因此,形参只有在该函数内有效。调用结束,返回调用函数后,则不能再使用该形参变量。2)实参能够是常量、变量、表示式、函数等。不论实参是何种类型量,在进行函数调用时,它们都必须含有确定值,方便
14、把这些值传送给形参。因此,应预先用赋值、输入等方法,使实参取得确定值。3)实参对形参数据传送是单向,即只能把实参值传送给形参,而不能把形参值反向地传送给实参。4)实参和形参占用不同内存单元,即使同名也互不影响5)在被定义函数中,必须指定形参类型。实参和形参类型应相同或赋值相容。第17页第17页main()int a=3,b=5;void swap(int,int);swap(a,b);printf(“a=%d,b=%dn”,a,b);void swap(int x,int y)int temp;temp=x;x=y;y=temp;printf(“x=%d,y=%d n”,x,y);是按值传递按
15、址传递放在指针里面讲。传递值35ab35xy 3tempMain()函数:调用Swap函数第18页第18页8.3.2 函数返回值通常,希望通过函数调用使通常,希望通过函数调用使主调函数主调函数得到一得到一个拟定值,这就是函数个拟定值,这就是函数返回值。返回值。阐明下列:阐明下列:1)函数返回值是通过)函数返回值是通过return语句语句取得。当不需返取得。当不需返回函数值时,可省去回函数值时,可省去return语句。语句。2)return语句后面能够语句后面能够有括号,也能够没有。有括号,也能够没有。如:如:returnz;return(z);3)return语句后面能够是变量,也能够是表示式
16、。语句后面能够是变量,也能够是表示式。如:如:return(xy?x:y);第19页第19页4)return语句返回值类型应与该函数语句返回值类型应与该函数类型一类型一致。致。否否则以函数类型为准。则以函数类型为准。5)若函数中)若函数中没有没有return语句语句,则该函数被调用,则该函数被调用后也会后也会带带回不拟定值回不拟定值。为了明确表示不需要函。为了明确表示不需要函数返回值,可数返回值,可以用以用“void”定义函数为定义函数为“无类无类型型”。凡不需要返回值函数,普通均定义为。凡不需要返回值函数,普通均定义为“void”类型。类型。6)一个函数能够)一个函数能够有一个以上有一个以上
17、return语句,执行到语句,执行到哪个哪个return语句,哪个语句,哪个return语句起作用。语句起作用。第20页第20页8.4 函数调用在在程程序序中中,是是通通过过对对函函数数调调用用来来执执行行函函数数体体,其其过过程程与与其其它语言子程序调用相同。它语言子程序调用相同。语言中,函数调用普通形式为:语言中,函数调用普通形式为:函数名函数名(实际参数表实际参数表)阐明:阐明:1)对于无参函数,尽管没有)对于无参函数,尽管没有“实参表实参表”,但也,但也不得省略不得省略括括号。号。2)“实参表实参表”中参数之间用中参数之间用逗号逗号分开。分开。3)实参与形参之间个数及类型必须)实参与形
18、参之间个数及类型必须一一相应。一一相应。4)对实参求值顺序是自左至右还是自右至左)对实参求值顺序是自左至右还是自右至左,视详细系统视详细系统而定。而定。TurboC和和MSC是按是按自右至左自右至左顺序求值。见顺序求值。见P162例例8.4第21页第21页8.4.2 函数调用方式按照函数在程序中出现位置,能够有下列三种调用方式:1)函数语句:C语言中函数能够只进行一些操作而不返回函语言中函数能够只进行一些操作而不返回函数值,这时函数调用可作为一条独立语句。如数值,这时函数调用可作为一条独立语句。如printf(“Cpragram”)gets(s););2)函数表示式:函数作为表示式一项,出现在
19、表示式中,函数作为表示式一项,出现在表示式中,以以函数返回值参与表示式运算函数返回值参与表示式运算。这种方式要求函数是。这种方式要求函数是有返有返回值回值。如:。如:c=2 max(a,b);3)函数参数:函数作为另一个函数调用函数作为另一个函数调用实际参数实际参数出现。这出现。这种情况是把该函数返回值作为实参进行传送,因此要求该种情况是把该函数返回值作为实参进行传送,因此要求该函数必须函数必须是有返回值是有返回值。如:。如:m=max(max(a,b),c);第22页第22页8.4.3 对被调用函数申明和函数原型对被调用函数申明对被调用函数申明在一个函数被另一个函数调用时,须具备下列条在一个
20、函数被另一个函数调用时,须具备下列条件:件:1)被调用函数已存在)被调用函数已存在2)假如被调函数为库函数,则应在文献开头)假如被调函数为库函数,则应在文献开头用用“#include”命令命令申明申明相应相应“头文头文件件”。如:。如:#include“stdio.h”#include“math.h”3)假如被调函数为假如被调函数为自定义函数自定义函数且其且其定义在主调函定义在主调函数定义之后数定义之后,则应在主调函数中阐明其类型(即,则应在主调函数中阐明其类型(即对被调用函数进行申明)。对被调用函数进行申明)。第23页第23页函数申明作用是把函数名、函数参数个数函数申明作用是把函数名、函数参
21、数个数和参数类型等信息和参数类型等信息告知编译系统告知编译系统,以便在调用时,以便在调用时,编译系统能正确辨认函数并检查调用编译系统能正确辨认函数并检查调用是否合法是否合法。函。函数申明又称函数原型。格式下列:数申明又称函数原型。格式下列:函数类型函数类型函数名(参数类型函数名(参数类型1,参数类型,参数类型2,参,参数类型数类型n);函数类型函数类型函数名(参数类型函数名(参数类型1,参数名,参数名1,参数类型,参数类型2,参数名,参数名2,参数类型,参数类型n,参数名,参数名n);假如不申明,系统无法在调用时对函数调用正假如不申明,系统无法在调用时对函数调用正确性进行检查,容易犯错。确性进
22、行检查,容易犯错。第24页第24页注意注意:函数定义和申明:函数定义和申明不是一回事不是一回事。定义是。定义是对函数功效确实对函数功效确实立立,包括指定函数名、函数值类型、形参及其类型、函数,包括指定函数名、函数值类型、形参及其类型、函数体等,它是一个完整、独立体等,它是一个完整、独立函数单位函数单位。而函数申明则是把。而函数申明则是把函数名字、函数类型以及形参类型、个数和顺序等函数名字、函数类型以及形参类型、个数和顺序等告知编告知编译系统译系统以便在调用时进行以便在调用时进行对照检查对照检查。如:main()double new_style(int,double);/*函数申明*/Doubl
23、e new_style(int a,double x)/*函数定义*/第25页第25页阐明:1)类型标识符类型标识符被调函数名被调函数名();这种申明这种申明形式也是合法,但不提倡。形式也是合法,但不提倡。2)假如被调函数为自定义函数且其定义)假如被调函数为自定义函数且其定义在主调函数定义之前,则在主调函数中可在主调函数定义之前,则在主调函数中可不必阐明其类型。由于编译程序已知道其不必阐明其类型。由于编译程序已知道其类型。类型。3)假如被调函数值是)假如被调函数值是整型或字符型整型或字符型,可,可不必申明类型不必申明类型,系统自动按整型阐明。,系统自动按整型阐明。第26页第26页4)假如在)假
24、如在所有被调函数定义之前、在文献开所有被调函数定义之前、在文献开头、在函数外部头、在函数外部已对被调函数作了类型阐已对被调函数作了类型阐明,则在各主调函数中可不必阐明其类型。明,则在各主调函数中可不必阐明其类型。见见P166例。例。charletter();floatf();inti();main()一开始就将所有要被一开始就将所有要被调用函数作出申明调用函数作出申明/主调函数中不必阐明它所调用函数类型主调函数中不必阐明它所调用函数类型/第27页第27页8.5 函数嵌套调用函数不允许嵌套定义,不过允许嵌套调用。函数嵌套调用是指,在执行被调用函数时,被调用函数又调用了其它函数。这与其它语言子程序
25、嵌套调用情形是类似,其关系可表示以下图:第28页第28页例:用弦截法求方程根。x35x2+16x80=0(1)取两个不同点x1、x2,假如f(x1)和f(x2)符号相反,则(x1,x2)区间内必有一个根。假如f(x1)与f(x2)同符号,则应改变x1、x2,直到f(x1)、f(x2)异号为止。注意x1、x2值不应差太大,以确保(x1,x2)区间只有一根。办法下列:第29页第29页(2)连接f(x1)和f(x2)两点,此线(即弦)交x轴于x.x点坐标可用下式求出:再从x求出f(x)。xyxf(x1)f(x)x1x2f(x2)第30页第30页3)若f(x)与f(x1)同符号,则根必在(x,x2)区
26、间内,此时将x作为新x1。假如f(x)与f(x2)同符号,则表示根在(x1,x2)区间内,将x作为新x2.4)重复环节(2)和(3),直到|f(x)|为止,为一个很小数,比如106。此时认为f(x)0.第31页第31页输入x1、x2,求f(x1)、f(x2)直到f(x1)与f(x2)异号求f(x1)与f(x2)连线与x轴交点xy=f(x),y=f(x1)y与y1同号x1=xy1=yx2=xy2=y直到|f(x)|0)/*f(x)与f(x1)同符号。*/y1=y;x1=x;else x2=x;while(fabs(y)=0.0001);return(x);第36页第36页main()/*主函数*
27、/float x1,x2,f1,f2,x;do printf(input x1,x2:n);scanf(%f,%f,&x1,&x2);f1=f(x1);f2=f(x2);第37页第37页 while(f1*f2=0);x=root(x1,x2);printf(A root of equation is%8.4f,x);运营情况下列:input x1,x2:2,6A root of equation is 5.000第38页第38页8.6 函数递归调用在调用一个函数过程中在调用一个函数过程中又出现直接或间接又出现直接或间接地调用地调用该函数该函数本身本身,称为函数递归调用。,称为函数递归调用。C
28、语言特点之一就在于允许函数递归调用。语言特点之一就在于允许函数递归调用。第39页第39页直接调用 int f(x)int x;int y,z;z=f(y);间接调用 int f1(x)int x;int y,z;z=f2(y);int f2(t)int t;int a,b;a=f1(y);第40页第40页显然:上述例子会无限递归(无限执行)。因此,在递归调用时都有条件限制。即:条件成立,调用递归,不然结束。例例8.7有有5人排成一队,从最后一人开始,其年龄均比人排成一队,从最后一人开始,其年龄均比前面人大前面人大2岁,而最前面人年龄是岁,而最前面人年龄是10岁岁,问问最后一人年龄是多少岁?最后
29、一人年龄是多少岁?main()age(5)age(4)+2age(n)n=5age(3)+2age(n)n=4age(2)+2age(n)n=3age(1)+2age(n)n=2age(1)age(n)n=1age(1)=10age(2)=12age(3)=14age(4)=16age(5)=18输出输出age(5)第41页第41页age(n)intn;intc;if(n=1)c=10;elsec=age(n1)+2;return(c);main()printf(“%dn”,age(5);运营结果运营结果:18第42页第42页例例8.8 用递归办法求用递归办法求n!1.从数学上定义从数学上定义
30、n!=1 (n=0,1)n(n1)!(n1)第43页第43页2.程序程序 float fac(n)int n;float f;if(n%cn,getone,putone);void hanoi(n,one,two,three)/*将n个盘从one借助two,移动three*/char one,two,three;第51页第51页 int n;if(n=1)move(one,three);else hanoi(n1,one,three,two);move(one,three);hanoi(n1,two,one,three);第52页第52页 main()int m;printf(input th
31、e number of disdes :);scanf(%d,&m);printf(The step to moving%3d disdes:n,m);hanoi(m,A,B,C);第53页第53页运营情况下列:input the number of disdes:3 The step to moving 3 diskes:A C A B C B A C B A B C A C第54页第54页两个函数:move(getone,putone)表示从getone 塔移一个盘子至putone塔 hanoi(n,one,two,three)表示n个盘子从one塔借助于two塔(空)移至three塔。调
32、用时塔用字符常量A,B,C 表示。第55页第55页8.7 8.7 数组作为函数参数数组作为函数参数分为两种情况:1.数组元素作为实参2.数组名同时为形、实参第56页第56页一、数组元素作为实参 由于数组元素与相同类型简朴变量地位完全同样;因此,数组元素作函数参数也和简朴变量同样,也是值单向传递第57页第57页例:设有两个同样大小一维数组,a10,b10将相应元素比较,统计a中不小于b中相应元素个数,小于个数,相等时个数。程序下列:main()int lange(int x,int y)int a10,b10,i,n=0,m=0,k=0;printf(enter array a:n);第58页第
33、58页 for(i=0;i10;I+)scanf(5,d,&ai);printf(n);printf(enter array b:n);for(i=0;i10;i+)scanf(%d,&bi);printf(n);for(i=0;ibi%d timesnai=bi%d timesnaik)printf(array a is larger than array bn);else if(ny)flag=1;else if(xy)flag=1;else flag=0;return(flag);第61页第61页运营情况下列:enter array a:1 3 5 7 9 8 6 4 2 0enter
34、array b:5 3 8 9 1 3 5 6 0 4 aibi 4 times ai=bi 1 times aibi 5 times array a is smaller than array b第62页第62页二、数组名作实、形参特点:直接用数组名作参数时,则为地址传送(不是值传送),即实参数组首地址传递给形参数组首地址。因此,实参、形参数组共享相同内存单元。1.形参数组可不指定大小,也可用另一参数作大小,以拟定使用实数组元素个数。2.形参、实参数组必须类型一致。3.多维数组方式同样,仅第一维大小阐明可省略第63页第63页例1.有一个一维数组score,内放10个学生成绩,求平均成绩。程序
35、下列:float average(array)float array10;int i;float aver,sum=array 0;for(i=1;i10;i+)sum=sum+arrayi;aver=sum/10;第64页第64页 return(aver);main()float score 10,aver;int i;printf(input 10 score:n);for(i=0;i10;i+)scanf(%f,&scorei);printf(n);第65页第65页 aver=average(score);printf(average score is%5.2f,aver);运营情况下列
36、:input 10 scores:100 56 78 98.5 76 87 99 67.5 75 97 average score is 83.40第66页第66页程序2.float average(array,n)int n;float array;int i;float aver,sum=array0;for(i=1;in;i+)sum=sum+arrayi;aver=sun/n;return(aver);第67页第67页 main()static float score_15=98.5,97,91.5,60,55static float score _2 10=67.5,89.5,99,
37、69.5,77,89.5,76.5,54,60,99.5printf(“the average of class A is%6.2fn”,average(score_1,5);printf(“the average of class B is%6.2fn”,average(score_2,10);第68页第68页运营结果下列:the average of class A is 80.40 the average of class B is 78.20第69页第69页8.8 局部变量和全局变量8.8.1局部变量局部变量概念:概念:是指在一定范围内有效变量是指在一定范围内有效变量。C语语言中,在下
38、列各位置定义变量均属于局部言中,在下列各位置定义变量均属于局部变量。变量。在函数体内定义变量,在函数体内定义变量,在本函数范围内有在本函数范围内有效,作用域局限于函数体内。效,作用域局限于函数体内。在复合语句内定义变量在复合语句内定义变量,在本复合语句范,在本复合语句范围内有效,作用域局限于复合语句内。围内有效,作用域局限于复合语句内。有参函数形式参数有参函数形式参数也是局部变量,只在其也是局部变量,只在其所在函数范围内有效。所在函数范围内有效。第70页第70页关于局部变量作用域还要说明以下几点:1主函数main()中定义内部变量,也只能在主函数中使用,其它函数不能使用。同时,主函数中也不能使
39、用其它函数中定义内部变量。因为主函数也是一个函数,与其它函数是平行关系。这一点是与其它语言不同,应给予注意。2允许在不同函数中使用相同变量名,它们代表不同对象,分派不同单元,互不干扰,也不会发生混同。第71页第71页比如:比如:intf1(inta)/*函数函数f1*/intb,c;/*a,b,c作用域:仅限于函数作用域:仅限于函数f1()中中*/intf2(intx)/*函数函数f2*/inty,z;/*x,y,z作作用用域域:仅仅限限于于函函数数f2()中中.x,y,z换换为为a,b,c也也能够能够*/main()intm,n;/*m,n作用域:仅限于函数作用域:仅限于函数main()中中
40、*/第72页第72页8.8.2全局变量全局变量全局变量:全局变量:在函数之外在函数之外定义变量。(所定义变量。(所有有函数前,函数前,各个函数各个函数之间,所有函数后之间,所有函数后)全局变量全局变量作用域:作用域:从定义全局变量位置从定义全局变量位置起到本源程序结束为止。起到本源程序结束为止。全局变量可被作用域内所有函数直接引全局变量可被作用域内所有函数直接引用。用。第73页第73页在函数fun()中,即使没有定义变量a,b,但由于它们定义在程序最前面,是全程变量,但凡在定义该变量后面定义函数均能够引用它们。#include “stdio.h”inta=3,b=5;/*在函数体外定义变量*/
41、Main()void fun();printf(“a=%d,b=%d n”,a,b);fun();printf(“a=%d,b=%d n”,a,b);void fun(void)int c;c=a;a=b;b=c;第74页第74页阐明:1.全局变量增长了函数间数据全局变量增长了函数间数据联系渠道联系渠道。由于同一文献中所。由于同一文献中所有函数都能引用全局变量值,当需要从一个函数中有函数都能引用全局变量值,当需要从一个函数中带回多个带回多个值值时,就能克服函数调用只能返回一个值不足。不成文约时,就能克服函数调用只能返回一个值不足。不成文约定:全局变量名定:全局变量名首字母大写首字母大写。见。见
42、P185例例8.152.如无必要如无必要,不要使用全局变量。由于全局变量既,不要使用全局变量。由于全局变量既减少程序清减少程序清晰性和函数通用性晰性和函数通用性,且又在程序所有执行过程中都且又在程序所有执行过程中都占用存占用存储空间。储空间。3.在文献开头定义外部变量才可在整个文献范围内使用,若在在文献开头定义外部变量才可在整个文献范围内使用,若在定义点之前函数需引用外部变量,则可用定义点之前函数需引用外部变量,则可用关键字关键字“extern”作作“外部变量阐明外部变量阐明”。第75页第75页注意:注意:外部变量定义和外部变量阐明并不是同一回事。外部变量外部变量定义和外部变量阐明并不是同一回
43、事。外部变量定义只能有一次定义只能有一次,它位置在所有函数之外。而同一程序中外它位置在所有函数之外。而同一程序中外部部变量阐明能够有多次变量阐明能够有多次,它位置在函数之内,它位置在函数之内(哪个函数要用就在哪个函数要用就在哪个函数中阐明哪个函数中阐明)。系统。系统依据依据外部变量外部变量定义定义(而不是依据外部变量而不是依据外部变量阐明阐明)分派存储单元分派存储单元。对外部变量初始化只能在。对外部变量初始化只能在“定义定义”时进行时进行,“extern”只是申明该变量是一个已在外部定义过变量而已。只是申明该变量是一个已在外部定义过变量而已。4.假如在同一源文献中,外部变量与局部变量假如在同一
44、源文献中,外部变量与局部变量同名同名,则在局部变,则在局部变量作用范围内,量作用范围内,外部变量不起作用外部变量不起作用。“屏蔽作用屏蔽作用”。见。见P187例例8.16第76页第76页外部变量定义与阐明。外部变量定义与阐明。intvs(intxl,intxw)externintxh;/*外部变量外部变量xh阐明阐明*/intv;v=xl*xw*xh;/*直接使用外部变量直接使用外部变量xh值值*/returnv;main()externintxw,xh;/*外部变量阐明外部变量阐明*/intxl=5;/*内部变量定义内部变量定义*/printf(xl=%d,xw=%d,xh=%dnv=%d,
45、xl,xw,xh,vs(xl,xw);intxl=3,xw=4,xh=5;/*外部变量外部变量xl、xw、xh定义定义*/第77页第77页程序区静态存储区动态存储区数据,变量存储内存分派8.9 变量存储类别(生存期、生命期)变量存储方式变量存储方式变量从变量从空间空间上分为局部变量、全局变量。上分为局部变量、全局变量。从变量从变量存在时间存在时间长短(即变量生存期)来划分,长短(即变量生存期)来划分,变量还能够分为:变量还能够分为:动态存储变量、静态存储变动态存储变量、静态存储变量。量。变量存储方式决定了变量生存期。变量存储方式决定了变量生存期。第78页第78页假假如如全全局局变变量量用用st
46、atic修修饰饰,并并不不是是说说是是静静态态,而而是是说说,只只对对本本模模块块有有效。效。自动(局部变量)(auto)动态存储方式 存储器(局部变量)(register)存储方式 静态(局部变量)(static)静态存储方式 静态全局变量(全局变量所有是静态 ,不必用static修饰)静态存储变量:存储于静态存储区,在程序整个运营过程中,始终占据固定内存单元。动态存储变量:存储于动态存储区,依据程序运营状态(如:函数调用)而暂时分派单元,且单元并不固定。每个变量或函数都有两个属性:每个变量或函数都有两个属性:数据类型和数据存储类别数据类型和数据存储类别第79页第79页8.9.2auto变量
47、变量auto型存储方式是型存储方式是C语言语言默认局部变量存储方式默认局部变量存储方式,也是局部变量,也是局部变量最最常使用常使用存储方式。存储方式。自动变量属于局部变量范围,作用域限于定义它函数或复合语句自动变量属于局部变量范围,作用域限于定义它函数或复合语句内。内。自动变量所在函数或复合语句执行时,系统动态为相应自动变量自动变量所在函数或复合语句执行时,系统动态为相应自动变量分派存储单元,当自动变量所在函数或复合语句执行结束后,自分派存储单元,当自动变量所在函数或复合语句执行结束后,自动变量失效,它所在存储单元被系统释放,因此本来自动变量值动变量失效,它所在存储单元被系统释放,因此本来自动
48、变量值不能保留下来。若对同一函数再次调用时,系统会对相应自动变不能保留下来。若对同一函数再次调用时,系统会对相应自动变量重新分派存储单元。量重新分派存储单元。定义格式定义格式:auto类型阐明类型阐明变量名;变量名;auto为自动存储类别关键词,为自动存储类别关键词,能够省略,缺省时系统默认能够省略,缺省时系统默认auto.第80页第80页8.9.3用用static申明局部变量申明局部变量静态局部变量定义格式静态局部变量定义格式:static类型阐明类型阐明变量名变量名=初始化值初始化值;static是静态存储方式关键词,不能省略。是静态存储方式关键词,不能省略。静态局部变量存储空间是在程序编
49、译时由系统静态局部变量存储空间是在程序编译时由系统分派,且在程序运营整个期间都固定不变。该分派,且在程序运营整个期间都固定不变。该类变量在其函数调用结束后仍然能够保留变量类变量在其函数调用结束后仍然能够保留变量值。下次调用该函数,静态局部变量中仍保留值。下次调用该函数,静态局部变量中仍保留上次调用结束时值。上次调用结束时值。静态局部变量初值是在程序编译时一次性赋予,静态局部变量初值是在程序编译时一次性赋予,在程序运营期间不再赋初值,以后若改变了值,在程序运营期间不再赋初值,以后若改变了值,保留最后一次改变后值,直到程序运营结束。保留最后一次改变后值,直到程序运营结束。第81页第81页 例:求n
50、!int fac(n)int n;static int f=1;f=fn;return(f);main()int i;for(i=1;i=5;i+)printf(%d!=%dn,i,fac(i);第82页第82页运营结果为:1!1 2!2 3!6 4!24 5!120 每一次调用fac(i),打印一个i!,同时保留这个i!值以便下次再乘(i+1)。第83页第83页8.9.4register变量变量register变量普通分派变量普通分派register给相应给相应变量。存储器比内存操作要快诸多,因变量。存储器比内存操作要快诸多,因此能够将一些此能够将一些需要重复操作局部变量存需要重复操作局部变