《c语言程序设计第8章.ppt》由会员分享,可在线阅读,更多相关《c语言程序设计第8章.ppt(91页珍藏版)》请在三一办公上搜索。
1、,1.我的程序有上百行,如何调试最方便?2.我想设计一个程序完成下面的计算:其中m,n为正整数,且mn 该如何设计程序最有效,第8章函数,本章学习要点:掌握函数的定义方法;掌握函数的类型和返回值;掌握形式参数与实际参数、参数传递;掌握函数的正确调用,了解函数的递归调用;掌握局部变量和全局变量、变量的存储类型,作用域和 生存期;,8.1 概述函数:英文名为Function,直译为“功能”,“函数”的意思.在C语言里,函数指的是实现一个特定功能的程序模块.它相当于其他语言中的子程序.1.一个C语言程序可分为若干个函数 2.每个程序有且只能有一个主函数(main),其它函数都都是子函数。3.每个C程
2、序由主函数调用子函数,子函数也可以相互调用.在程序设计中,常将一些常用的功能模块编写成函数,放在函数库中供公共选用.,main,函数a,函数e,函数f,函数g,.,.,.,函数b,函数h,函数I,函数J,.,.,.,函数c,函数K,函数L,函数M,.,.,.,函数a,函数M,函数e,说明:1)源程序文件可由一个或多个函数组成,其中主函数是不可缺省的.源程序文件是编译单位,函数不是编译单位;2)一个C程序由一个或多个源程序文件组成.较大的C程序,常将一些函数和其他内容分别放在若干源文件中,再由若干源文件组成一个C程序.最简单的情况,一个C程序由一个源程序组成,这个源程序中只包含了一个函数-主函数
3、;3)C程序的执行从main函数开始,调用其他函数后仍回到main函数,程序在main函数结束时结束 4)所有子函数都是平行的,任何子函数都不属于其它函数 5)所有函数在定义时是相互独立的,函数之间可以相互引用但不能嵌套定义;,例:8-1#includevoid printstar()printf(“*n”);void print_message()printf(“How do you do!n”);void main()printstar();print_message();printstar();,说明1)每个程序有且只能有一个主函数(main),其它函数都是子函数。2)C程序的执行从ma
4、in函数开始,调用其他函数后仍回到main函数,程序在main函数结束时结束 3)所有子函数都是平行的,任何子函数都不属于其它函数,*How do you do!*,函数的分类:1)从用户使用的角度函数可分为:标准函数,即库函数.由系统提供,用户不必定义,直接使用;用户自定义函数.由用户根据需要,自行编写,以解决专门需要;2)从函数的形式分,函数可分为:无参数函数.在调用无参函数时,主函数并不将数据传送给被调用函数,一般用来执行指定的一组操作.无参函数可以带回也可以不带回函数值,一般以后者居多;如:printstar()有参函数.在调用函数时,在主函数和被调用函数之间有数据传递;如:max(a
5、,b),8.2 函数定义的一般形式,函数定义的一般形式:类型标识符 函数名(形式参数说明)函数体 形式参数说明方法:类型标识符 变量名,类型说明符 变量名1.无参函数的定义形式 类型标识符 函数名()函数体2.有参函数定义的一般形式 类型标识符 函数名(形式参数表列)函数体 3.空函数定义的一般形式类型标识符 函数名(形式参数表列),例如:int max(int x,int y)int z;z=xy?x:y;return(z);例如:void printhello()printf(“Hello,wordn”);,函数定义的一般形式:类型标识符 函数名(形式参数说明)函数体 形式参数说明方法:类
6、型标识符 变量名,类型说明符 变量名1.无参函数的定义形式 类型标识符 函数名()函数体2.有参函数定义的一般形式 类型标识符 函数名(形式参数表列)函数体,例如:int max(int x,int y)int z;z=xy?x:y;return(z);,例如:void printhello(char name)printf(“Hello,%sn”,name);,例如:dummy();,3.空函数定义的一般形式类型标识符 函数名(形式参数表列),例如:int max(int x,int y)int z;z=xy?x:y;return(z);例如:void printhello(char nam
7、e)printf(“Hello,%sn”,name);,一般情况下,函数体由两部分组成:局部变量说明 语句 局部变量:函数体内定义的变量。其有效范围仅限于所在的函数内部,离开函数体则无意义。,8.3 函数参数和函数的值,一个C程序由若干个函数组成,各函数调用时经常需要传递一些数据。即调用函数把数据传递给被调用函数,经被调用函数处理后,得到一个确定的结果,在返回调用函数时,把这结果带回调用函数。,例8.2 调用函数时的数据传递#includeint max(int x,int y)int z;z=xy?x:y;return(z);void main()int a,b,c;scanf(“%d,%d
8、”,printf(“Max is%d”,c),c=max(a,b);,int max(int x,int y)int z;z=xy?x:y;return(z);,主调函数,被调用函数,Z,a.b,各函数信息往来是由参数传递和返回语句实现,8.3 函数参数和函数的值,8.3.1 形式参数和实际参数 函数参数:用于函数间数据传递1、形式参数(形参):函数定义时设定的参数。下例中,函数头int max(int x,int y)中x,y就是形参,它们的类型都是整型。2、实际参数(实参):调用函数时所使用的实际的参数。下例中,主函数中调用max函数的语句是:nmax=max(a,b);其中a,b就是实参
9、,它们的类型都是整型。,实参a,b,形参x,y,运行情况如下:7,8Max is 8,例8.2 调用函数时的数据传递int max(int x,int y)int z;z=xy?x:y;return(z);main()int a,b,c;scanf(“%d,%d”,printf(“Max is%d”,c),关于形参和实参的说明(1)函数调用前形参不占内存。只用在发生函数调用时,形参才被分配内存单元.在调用结束后,形参所占的内存单元也被释放;(2)在被定义的函数中,必须指定形参的类型,只能是变量或数组(3)实参可以是常量,变量或表达式,如:max(3,a+b).但要求它们有确定的值.在调用时将实
10、参的值赋给形参.(4)实参和形参的类型应相同或赋值兼容,否则会出错;字符型和整形可以相互匹配。(5)C语言规定,实参对形参变量的数据传递是“值传递”,即单向传递,只能由实参传给形参,而不能由形参传回来.它仅由参数的位置确定,与名字无关。,例8.2 调用函数时的数据传递int max(int x,int y)int z;z=xy?x:y;return(z);main()int a,b,c;scanf(“%d,%d”,printf(“Max is%d”,c),又将函数max作变形会怎样?#include int max(int b,int a)int c;c=ab?a:b;return(c);ma
11、in()int a,b,c;scanf(“%d,%d”,printf(“Max is%d”,c),将函数max作变形会怎样?#include int max(int x,int y)x=xy?x:y;return(x);main()int a,b,c;scanf(“%d,%d”,printf(“Max is%d”,c),例:读程序,写出结果#includeint 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%dn,a,b,c);,sum of 1,3
12、 is 4,8.3.2 函数的返回值 通常,希望通过函数调用使主调函数得到一个确定的值,这就是函数的返回值.说明:(1)函数的值也就是函数的返回值。函数的返回值是通过函数中的return语句获得的.,int max(int x,int y)int z;z=xy?x:y;return(z);main()int a,b,c;scanf(“%d,%d”,printf(“Max is%d”,c),8.3.2 函数的返回值 通常,希望通过函数调用使主调函数得到一个确定的值,这就是函数的返回值.说明:(1)函数的返回值是通过函数中的return语句获得的.(2)一个函数中可以有一个以上的return语句,
13、但不论执行到哪个return都将结束函数调用返回主调函数;,int max(int x,int y)if(xy)return x;else return y;main()int a,b,c;scanf(“%d,%d”,printf(“Max is%d”,c),8.3.2 函数的返回值 通常,希望通过函数调用使主调函数得到一个确定的值,这就是函数的返回值.说明:(1)函数的返回值是通过函数中的return语句获得的.(2)一个函数中可以有一个以上的return语句,但不论执行到哪个return都将结束函数调用返回主调函数;(3)return语句后的括弧可以不要;例如:return z;和retu
14、rn(z);是等效的.return后面的值可以是一个表达式。(4)函数返回值的类型应当在定义函数值时指定;在定义函数时对函数值说明的类型一般应该和return语句中的表达式类型一致,如果不一致,则以函数类型为准.,int max(int x,int y)return(xy?x:y;);main()int a,b,c;scanf(“%d,%d”,printf(“Max is%d”,c),(6)为了明确表示“不带回值”,可以用“void”定义无类型.例如:例8.1中的定义为:void printstar()void print_message()这样,系统就保证不使函数带回任何值,即禁止在调用函数
15、中使用被调用函数的返回值.例如:a=printfstar();b=print_message();是不合法的.,8.4 函数的调用,8.4.1 函数调用的一般形式函数调用的一般形式为:函数名(实参表列)说明:(1)如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略;(2)如果实参表列多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应一致;在Turbo C中,对实参求值的顺序是按自右至左顺序求值的.,例8.2 调用函数时的数据传递int max(int x,int y)int z;z=xy?x:y;return(z);main()int a,b,c;scanf(“%d,%d”
16、,printf(“Max is%d”,c),8.4.1 函数调用的一般形式函数调用的一般形式为:函数名(实参表列)说明:(1)如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略;(2)如果实参表列多个实参,则各参数间用逗号隔开,实参与形参的个数应相等,类型应一致;在Turbo C中,对实参求值的顺序是按自右至左顺序求值的.,例:读程序,写出结果#includeint f(int a,int b)int c;if(ab)c=1;else if(a=b)c=0;else c=-1;return c;void main()int i=2,p;p=f(i,+i);printf(“%d”,p);
17、,8.4.1 函数调用的一般形式函数调用的一般形式为:函数名(实参表列)说明:(1)如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略;(2)如果实参表列多个实参,则各参数间用逗号隔开,实参与形参的个数应相等,类型应一致;在Turbo C中,对实参求值的顺序是按自右至左顺序求值的.,例:读程序,写出结果#includeint f(int a,int b)int c;if(ab)c=1;else if(a=b)c=0;else c=-1;return c;void main()int i=2,p,j;j=+i;p=f(i,j);printf(“%d”,p);,8.4.2 函数调用的方式
18、按函数在程序中出现的位置来分,可以有以下三种函数调用方式:1.函数语句把函数调用作为一个语句.如例8.1中的printstar();这种不要求函数带回值,只要求函数完成一定的操作;2.函数表达式函数出现在一个表达式中,这种表达式称为函数表达式,这是要求函数带回一个确定的值参加表达式的运算.例如:m=max(a,b);c=2*max(a,b);3.函数参数函数调用作为一个函数的实参.这种调用的实质也是函数表达式调用的一种.例如:m=max(a,max(b,c);printf(“max is%d”,max(a,b);其中max(b,c)是一次函数调用,它的值作为max另一次调用的实参。,函数调用的
19、执行过程 按从左向右的顺序,计算实参中各表达式的值按照位置,将实参的值一一对应地传给形参 执行被调用函数 当遇到return(表达式)语句时,计算表达式的值,并返回主调函数。,例:读程序,写出结果#include int iabs(float x)return(x0?x;-x);void main()float x=-1,y;y=iabs(x)printf(“x=%f,iabs(x)=%fn,x,y);,8.4.3 对被调用函数的声明和函数原型,在一个函数中调用另一个函数需要具备的条件:(1)首先被调用的函数必须是已经存在的函数(库函数或用户自己定义的函数);(2)如果使用库函数,一般还应该在
20、本文件开头用#include命令将调用有关库函数时所用到的信息“包含”到本文件中来。,例如(先定义后使用)#includefloat f(float x)return 2*x*x+3*x+1);void main()float x;scanf(f,例如(使用库函数)#include#includevoid main()float x,y;scanf(f,(3)如果使用用户自己定义的函数,被调函数定义在主调函数之后,一般还应该在主调函数中对被调用函数作声明。(函数声明也称为函数原形),对被调用函数的声明。,从此行开始为对被调用函数的定义。包括函数首部、函数体等。,例(后定义使用原型说明)#inc
21、ludevoid main()float f(float);float x;scanf(f,函数原型的形式为:函数类型 函数名(参数类型1,参数类型2),在函数声明中可以不写形参名,而只写形参的类型。如可以这样进行函数声明:float add(float,float);函数原型的一般形式为:(1)函数类型 函数名(参数类型1,参数类型2)(2)函数类型 函数名(参数类型1 参数名1,参数类型2 参数名2)编译系统不检查参数名。函数声明可以写成:float add(float a,float b);,应当保证函数原型与函数首部写法上的一致,即函数类型、函数名,参数个数,参数类型和参数顺序必须相同
22、。函数调用时函数名、实参个数应与函数原型一致。实参类型必须与函数原型中的参数类型赋值兼容。如果被调用函数的定义出现在主调函数之前,可以不必加以声明。,8.3 函数参数和函数的值,8.3.1 形式参数和实际参数1、形式参数(形参):函数定义时设定的参数。2、实际参数(实参):调用函数时所使用的实际的参数。关于形参和实参的说明(1)函数调用前形参不占内存。只用在发生函数调用时,形参才被分配内存单元.在调用结束后,形参所占的内存单元也被释放;(2)C语言规定,实参对形参变量的数据传递是“值传递”,即单向传递,只能由实参传给形参,而不能由形参传回来.它仅由参数的位置确定,与名字无关。,8.4 函数的调
23、用,8.4.1 函数调用的一般形式函数调用的一般形式为:函数名(实参表列)说明:(1)如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略;(2)如果实参表列多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应一致;在Turbo C中,对实参求值的顺序是按自右至左顺序求值的.,如果使用用户自己定义的函数,被调函数定义在主调函数之后,一般还应该在主调函数中对被调用函数作声明。(函数声明也称为函数原形)函数原型的一般形式为:(1)函数类型 函数名(参数类型1,参数类型2)(2)函数类型 函数名(参数类型1 参数名1,参数类型2 参数名2)如果被调用函数的定义出现在主调函数之前,可以
24、不必加以声明。,8.4.3 对被调用函数的声明和函数原型,例:编写求 的程序分析:重复三次求阶乘运运算,只是每次的值不同。将求阶乘的过程编成一个函数fac,以不同的参数值k来调用函数,fac,k,k!,#include#includefloat fac(int k)float t=1;int i;for(i=2,i=k;i+)t*=i;return t;void main()float c;int m,n;scanf(“%d%d”,例:编写求 的程序分析:重复三次求阶乘运运算,只是每次的值不同。将求阶乘的过程编成一个函数fac,以不同的参数值k来调用函数,fac,k,k!,#include#i
25、ncludevoid main()float c;int m,n;float fac(int);/float fac(int x);scanf(“%d%d”,8.5函数的嵌套调用,C语言的函数定义都是互相平行、独立的。C语句不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。,#includevoid printstar()printf(“*n”);void print_message()printf(“How do you do!n”);printstar();void main()printstar();print_message();,例 8.用弦截
26、法求方程 f(x)=x3-5x2+16x-80=0 的根,例:读程序,写出结果#include float myfabs(float x)return x0?x:-x;float myfunc(float r)return 2*myfabs(r)+1;void main()float f=0.5;printf(%f,myfunc(f);,程序运行结果是:2.000000,8.6函数的递归调用,许多数学函数都是用递归形式定义的,8.6函数的递归调用,在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。例如:int f(int x)
27、int y,z;z=f(y);return(2*z);在调用函数f 的过程中,又要调用f 函数,这是直接调用本函数.见图。,开始,调用f函数,f 函数,在调用 f1 函数过程中要调用 f2 函数,而在调用f2函数过程中又要调用 f1 函数。从图上可以看到,这两种递归调用都是无终止的自身调用。可以用if语句来控 制,只有在某一条件成立时才继续执行递归调用,否则就不再继续。,开始,调用f2函数,开始,调用f1函数,f1 函数,f2 函数,例8.7 有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,说比 第1
28、个人大 2岁。最后问第1个人,他说是10岁。请问第5个人多大。显然,这是一个递归问题。每一个年龄都比其前1个人的年龄大2岁即 age(5)=age(4)+2 age(4)=age(3)+2 age(3)=age(2)+2 age(2)=age(1)+2 age(1)=10因此,除了第一个人,其余的人的年龄都需用递归法求出。,执行过程如下:,age函数n=5,main,age(5),输出age(5),c=age(4)+2,age函数n=4,c=age(3)+2,age函数n=3,c=age(2)+2,age函数n=2,c=age(1)+2,age函数n=1,c=10,age(2)=12,age(
29、3)=14,age(4)=16,age(5)=18,int age(int n)/*递归函数*/int c;if(n=1)c=10;else c=age(n-1)+2;return(c);main()/*主函数*/printf(“%d”,age(5),int age(int n)/*递归函数*/int c;if(n=1)c=10;else c=age(n-1)+2;return(c);main()/*主函数*/printf(“%d”,age(5),递归结束条件,递归公式,递归函数编写总结:1)采用if语句格式;2)两要素:递归结束条件 递归公式3)一般形式 if(递归结束条件)else 递归公
30、式 4)递归公式中包含递归函数名,但参数不同5)if语言前的语句在递归进入时执行,if语句后的语句在递归返回时执行,例8.8用递归的方法求n!递推法:从1开始,乘2,乘3,一直乘到n。其基本 原理是从一个已知事实推出下一个事实。递归法:当n=1或0时,n!=1;当n1时,n!=n*(n-1)!。,float fac(int n)float f;if(n0)printf(“n0,dataerror!”);else if(n=0|n=1)f=1;elsef=fac(n-1)*n;return(f);,main()int n;float y;printf(“input a integer numbe
31、r:”;scanf(“%d”,8.7 数组作为函数参数,1.数组元素作为函数实参数组元素作函数实参:“值传递”方式,即单向传递。例8.10 有两个数组a,b,各有10个元素,将它们对应地逐个相比。如果a数组中的元素大于b数组中的相应元素的数目多于b数组中元素中大于a数组中相应元素的数目,则认为a数组大于b数组,并分别统计出两个数组相应元素大于、等于、小于的次数。分析:1)对于两个数组中的元素比较结果,我们可以用3个变量来记录;2)为了保持程序的简洁,用一个函数来进行数组元素的比较,比较结果用该函数的返回值表示。,main()int large(int x,int y);int a10,b10,
32、i,n=0,m=0,k=0;printf(“enter array a:n”);for(i=0;ibi%d imesn ai=bi%d imesn aik)printf(“array a is larger than array bn”);if(ny)flag=1;else if(xy)flag=-1;else flag=0;return(flag);,输入数组a和数组b的数据,调用large函数进行数组元素比较,large函数,返回比较结果,2.数组名可作函数参数数组名可作函数参数,此时实参与形参都应用数组名。说明:1)用数组名作函数参数,应该在主调函数和被调用函数分别定义数组,不能只在一方
33、定义;2)实参数组与形参数组类型应一致,否则出错;3)实参数组与形参数组大小可以一致也可以不一致,C编译对形参数组大小不作检查,只是将实参数组的首地址传给形参数组;,4)形参数组也可以不指定大小,在定义数组时在数组名后面跟一个空的方括弧,为了在被调用函数中处理数组元素的需要,可以另设一个参数,传递数组元素的个数。5)数组名作函数实参,不是值传递而是地址传递,实参和形参数组将共占用同一段内存单元.如果形参数组中各元素发生变化会使实参数组元素的值同时发生变化。,#includeint max(int x,int n)int i,m;m=x0;for(i=1;in;i+)if(mxi)m=xi;ma
34、in()int a10,i;printf(“Enter 10 integer:n);for(i=0;i10;i+)scanf(“%d”,#includeint max(int x10)int i,m;m=x0;for(i=1;i10;i+)if(mxi)m=xi;main()int a10,i;printf(“Enter 10 integer:n);for(i=0;i10;i+)scanf(“%d”,例8.11 有一个一维数组score,内放10个学生成绩,求平均成绩。float average(float array10)int i;float aver,sum=array0;for(i=1
35、;i10;i+)sum=sum+arrayi;aver=sum/10;return(aver);main()float score10,aver;int i;printf(“input 10 score:n”);for(i=0;i10;i+)scanf(“%f”,例8.13 用选择法对数组中10个整数按由小到大排序。选择法介绍:1)将10个数进行相互比较,找出最小数,将之与a0对换;2)再将a1到a9的数进行比较,找出最小数,将之与a1对换;3),共比较9轮,最后得到排序后的结果;以5个数为例说明:a0 a1 a2 a3 a4 3 6 1 9 4 未排序时的情况 1 6 3 9 4 将5个数中
36、最小数1与a0对换 1 3 6 9 4 将余下的4个数中最小数3与a1对换 1 3 4 9 6 将余下的3个数中最小数4与a2对换 1 3 4 6 9 将余下的2个数中最小数6与a3对换至此完成排序,#includevoid sort(int array,int n)int i,j,t;for(i=0;in-1;i+)for(j=i+1;jn;j+)if(arrayjarrayi)t=arrayj;arrayj=arrayi;arrayi=t;void main()int a10,i;for(i=0;i10;i+)scanf(%d,例:以下程序的输出结果是()。long fun(int n)l
37、ong s;if(n=1|n=2)s=2;else s=n-fun(n-1);return s;main()printf(%ldn,fun(3);A)1B)2C)3D)4,A,递归函数编写总结:1)采用if语句格式;2)两要素:递归结束条件 递归公式3)一般形式 if(递归结束条件)else 递归公式,复习,1.数组元素作为函数实参数组元素作函数实参:“值传递”方式,即单向传递。2.数组名可作函数参数 1)数组名可作函数参数,此时实参与形参都应用数组名。1)形参数组也可以不指定大小,在定义数组时在数组名后面跟一个空的方括弧,为了在被调用函数中处理数组元素的需要,可以另设一个参数,传递数组元素的
38、个数。2)数组名作函数实参,不是值传递而是地址传递,实参和形参数组将共占用同一段内存单元.如果形参数组中各元素发生变化会使实参数组元素的值同时发生变化。,现有以下程序:#includeinverse(char str)char t;int i,j;for(i=0,j=strlen(str);istrlen(str)/2;i+,j-)t=stri;stri=strj-1;strj-1=t;main()char str100;scanf(“%s”,str);inverse(str);printf(“%sn”,str);如果输入an anple,该程序的输出结果是()。A)an anpleB)elp
39、na naC)anD)na,D,复习,3.用多维数组名作函数参数 用多维数组作为实参和形参,遵循“地址传递”,形参值变化影响实参值变化;在被调用函数中对形参数组定义时可以指定每一维的大小,也可以省略第一维的大小说明;判断以下定义形式哪些是正确的?int max(int x35)int max(int x 5)int max(int x3)int max(int x)从实参传递来的是数组起始地址,在内存中按数组排列规则存放(按行存放),并不区分行和列,与此不能省略第二维;注意:二维数组的一维表示时,其元素与一维数组等同。,例:有一个34的矩阵,求最大元素的值#include int max(in
40、t x 4)int i,j,m;m=x00;for(i=0;i3;i+)for(j=0;j4;j+)if(mxij)m=xij;return m;void main()int a34=1,3,5,7,2,4,6,8,15,13,17,5;printf(“Max is%d”,max(a);,实参与形参的小结,8.8 局部变量和全局变量,8.8.1 局部变量 在一个函数内部定义的变量是内部变量,只在本函数范围内有效,在此函数之外是不能使用这些变量,成为局部变量。,float f1(int a)int b,c;char f2(int x,int y)int i,j;main()int m,n;,变量
41、a,b,c有效,变量x,y,i,j有效,变量m,n有效,下列变量是局部变量:1、在一个函数内部定义的变量2、函数的形式参数3、在某个复合语句中定义的变量,说明:(1)主函数main中定义的变量(如m,n)也只在主函数中有效,而不因为在主函数中定 义而在整个文件成程序中有效,这是和Pascal不同的。主函数也不能使用其他函数中 定义的变量。(2)不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。(3)形式参数也是局部变量,例如fl函数中的形参a,也只在fl函数中有效。其他函数不能调用。,(4)在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也可称
42、为“分程序”或“程序块”,main()int a,b;int c;c=a+b;,变量c只在复合语句内有效,离开后就无效,释放内存单元。,变量a,b在此范围有效,(5)如果局部变量的有效范围有重叠,则有效范围小的优先main()int a,b,c;int c;c=a+b;,局部优先,c有效,8.8.2 全局变量,程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量称为外部变量,外部变量是全 局变量(也称全程变量)。全局变量可以为本文件中其他函数所共用。它的有效范围为从 定义变量的位置开始到本源文件结束。,int p=1,q=5;fl
43、oat f1(int a)int b,c;char c1,c2;char f2(int x,int y)int i,j;main()int m,n;,全局变量c1,c2的作用范围,全局变量p,q的作用范围,在一个函数中,既可以使用本函数中的局部变量,也可以使用有效的全局变量,说明:1.全局变量的主要作用时增加了函数间数据联系的渠道。同一文件中的函数都可以使用全局变量,如果在一个函数中改变了全局变量的值,将影响到其他函数。为了便于区别全局变量和局部变量,一般约定全局变量的第一个字母用大写;2.建议不在必要时不要使用全局变量,因为:全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开
44、辟单元。它使函数的通用性降低了 使用全局变量过多,会降低程序的清晰性3.如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。,利用全局变量可以在函数间传递数据例8.15 有一个一维数组,内放1个学生成绩。写一个函数,求出平均分、最高分和最低分。#include float Max,Min;void main()float average(float array,int n);float ave,score10;int;for(;)scanf(“”,score);ave=average(score,10);printf(“max=%6.2fn
45、min=%6.2fn average=%6.2fn“,Max,Min,ave);,float average(float array,int n)int;float aver,sum=array;Max=Min=array;for(=;)if(arrayiMax)Maxarrayi;else if(arrayiMin)Min arrayi;sum=sum+arrayi;aversum10;return(aver);,例8.16 外部变量与局部变量同名。int a=3,b=5;max(int a,int b)int c;c=ab?a:b;return(c);main()int a=8;print
46、f(“%d”,max(a,b);结果为:8,a,b为全局变量,a,b为局部变量,形参a,b作用范围,局部变量a作用范围全局变量b作用范围,下列程序执行后输出的结果是()。int d=1;fun(int p)int d=5;d+=p+;printf(“%d”,d);main()int a=3;fun(a);d+=a+;printf(“%dn”,d);A)84B)96C)94D)85,A,d=d+(p+)=5+3=8,d=d+(a+)=1+3=4,8.9 变量的存储类别,8.9.1 动态存储方式与静态存储方式按作用域来分:全局变量和局部变量。按变量值生存期来分:静态存储方式、动态存储方式。静态存储
47、方式:在程序编译时分配固定的存储空间的方式 动态存储方式:在函数调用的时候动态分配存储空间的方式。内存中的供用户使用的存储空间的情况。这个存储空用户区间。分为三部分,见图。,程序区,静态存储区,动态存储区,用户区,数据存放区,全局变量全部存放在静态存储区:非动态地进行分配和释放,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元。动态存储区中存放以下数据:1.函数形式参数。2.自动变量(未加static声明的局部变量)。3.函数调用别的现场保 护和返回地址等。动态地进行分配和释放。在函数调用开始时分配动态存储空间,函数结束时释放这些空间。变量和函数
48、的属性:数据类型 整型、字符型等 自动(auto)静态(static)数据存储类别:静态存储类和动态存储类。寄存器(register)外部(extern),8.9.2 auto变量,函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中在函数调用结束时就自动释放这些存储空间,因此这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。例如:int f(int a)/*定义f函数,a为形参*/auto int b,c=3/*定义b,c为自动变量*/形参a,变量b、c都是自动变量。调用 该函数,系统给它们分配存储空间,函数调用结束时自动释
49、放存储空间 实际上,关键字“auto”可以省略,auto不写则隐含确定为“自动存储类别”。,8.9.3 用static声明局部变量,有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。这时就应该指定该局部变量为”静态局部变量”,用关键字static。进行声明。例8.17 f(int a)auto int b=0;static int c=3;b=b+1;c=c+1;return(a+b+c);main()int a=2,i;for(i=0;i3;i+)printf(“%d”,f(a);,运行结
50、果为:7 8 9,说明:1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在程序整个运行期间都不释放;2)对静态局部变量是在编译时赋初值的,在程序运行时它已有初值,以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。自动变量赋初值,不是在编译时进行,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句;3)如在定义局部变量时不赋初值的话,对静态局部变量,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。,4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。一般在以