版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第8章函数概述函数的定义函数的返回值函数的调用函数参数及其传递方式函数的嵌套与递归调用数组作为函数参数变量的存储属性内部函数和外部函数8.1
概述一、模块化程序设计和函数
1、模块化程序设计基本思想:将一个大的程序按功能分割成一些小模块。特点:各模块相对独立、功能单一、结构清晰、接口简单控制了程序设计的复杂性缩短开发周期避免程序开发的重复劳动易于维护和功能扩充开发方法:自上向下,逐步分解,分而治之2、C是模块化程序设计语言
一个较大的程序一般由几个程序模块组成,每个程序模块完成一定的功能。在C语言中,这种功能模块就是由函数来实现的。2、C是模块化程序设计语言
C是函数式语言,一个源程序文件是1个编译单位,
必须有且只能有一个名为main的主函数
C程序的执行总是从main函数开始,在main中结束,不管其位置所有的函数独立定义,互相调用。main()函数可以调用任意其它函数,其它函数之间可以相互调用,但任何函数都不能调用
main()函数。源程序文件1预编译命令说明部分执行部分函数1函数n源程序文件i源程序文件nC程序二、数学函数和C函数如数学中有:f(x)=f(0)=f(1)=f(2)=定义函数调用函数形式参数实际参数函数值=0=2.23607=4二、数学函数和C函数再如:g(x,y)=g(3,4)==5定义函数调用函数两个形式参数两个实际参数函数值三、函数分类1、按照使用特点标准函数(库函数):由系统提供,解决公共问题,使用时要包含相应的库文件。自定义函数:由用户自己定义,解决特定的问题有参函数:主调函数与被调函数之间有参数传递无参函数:主调函数与被调函数之间无参数传递 通常来执行一组操作2、按照函数形式使用库函数应注意:1、函数功能2、函数参数的数目和顺序,及各参数意义和类型3、函数返回值意义和类型4、需要使用的包含文件
函数必须定义才能够调用,在函数定义时,要对函数的类型、形参及其类型、函数中所要完成的操作、返回的函数值进行一系列的规定,根据这些规定才能够正确的对函数进行调用。其中:函数中所完成的操作是函数的核心部分,是由程序语句实现的;函数及形参的类型是正确使用函数的依据;函数返回的值是调用函数后所得到的结果。8.2
函数的定义8.2
函数的定义一般定义格式合法标识符函数体函数类型函数名(形参类型说明表){
说明部分 语句部分}例空函数
dummy(){}函数体为空函数返回值类型缺省int型无返回值void例:有参函数
intmax(intx,inty){intz;z=x>y?x:y;return(z);}例:无参函数
voidprintstar(){printf(“**********\n”);}或
voidprintstar(void){printf(“**********\n”);}8.3
函数的参数和函数的值(返回值)一、函数的参数包括形参和实参,要正确进行函数的调用,必须确定参数的数量和类型。函数定义时函数名后的括号中的变量叫形式参数。
形式参数只能是变量和数组名函数调用时函数名后的括号中的参数叫实际参数。
实际参数可以是常量、变量、数组名、表达式和函数值。8.3函数的参数和函数的值(返回值)二、返回语句形式:return(表达式);
或
return表达式;
或
return;功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数说明:函数返回值的类型:所返回函数值的类型取决于函数的类型。若函数类型与return语句中表达式值的类型不一致,按前者为准,自动转换------函数调用转换void型函数:函数无返回值,可以不要return语句c=max(a,b);(main函数)(max函数)intmax(intx,inty){intz;z=x>y?x:y;return(z);}例:比较两个数并输出大者voidmain(){inta,b,c;scanf("%d,%d",&a,&b);c=max(a,b);printf("Maxis%d",c);}intmax(intx,inty){intz;z=x>y?x:y;return(z);}形参实参例:函数返回值类型转换voidmain(){floata,b;intc;scanf("%f,%f",&a,&b);c=max(a,b);printf("Maxis%d\n",c);}intmax(floatx,floaty){floatz;z=x>y?x:y;
return(z);}3.1,5.2↙Maxis58.4
函数的调用
函数定义后,并不被执行,形参和函数中的其它变量并不分配存储单元。只有当调用函数时,程序才转到函数去执行。这时,为形参及函数中的其它变量分配临时存储单元,并将实参的值按顺序传递给形参。一、调用形式
函数名(实参表);
说明: 1、实参与形参个数相等,类型一致,按顺序一一对应
2、实参表求值顺序,因系统而定(大多数C版本
自右向左)main(){inti=2,p;p=f(i,++i);printf("%d",p);}intf(inta,intb){intc;if(a>b)c=1;elseif(a==b)c=0;elsec=-1;return(c);}例:参数求值顺序(turboc自右向左)main(){inti=2,p;p=f(i,i++);printf("%d",p);}intf(inta,intb){intc;if(a>b)c=1;elseif(a==b)c=0;elsec=-1;return(c);}运行结果:0运行结果:1二、调用方式:按照函数在程序中出现的位置来分函数语句:例:printstar();printf(“Hello,World!\n”);函数表达式:例:m=max(a,b)*2;函数参数:例:printf(“%d”,max(a,b));m=max(a,max(b,c));一般形式:函数类型函数名(形参类型形参名1,…..);
或函数类型函数名(形参类型,…..);作用:告诉编译器函数类型、参数个数及类型,以便检验。函数定义与函数声明不同函数声明位置:函数内或外下列情况下,可不作函数说明若函数返值是int型,C语言允许,但不提倡。被调用函数定义出现在主调函数之前。三、函数声明的概念函数应当先定义后调用。当函数的定义出现在调用之后,应当在调用之前进行声明。函数声明例:函数声明举例main(){inta,b;floatz;
scanf("%d%d",&a,&b);z=average(a,b);printf("a=%d,b=%d,c=%.2f\n",a,b,z);}floataverage(inta,intb){floatc;c=(a+b)/2.0;return(c);}floataverage(inta,intb);函数的调用和声明的格式有什么区别?定义在后的非整型函数调用在前在执行语句之前对函数声明floataverage(int,int);例:函数声明举例main(){inta,b;floatz;
scanf("%d%d",&a,&b);z=average(a,b);printf("a=%d,b=%d,c=%.2f\n",a,b,z);}floataverage(inta,intb){floatc;c=(a+b)/2.0;return(c);}定义在前的非整型函数调用在后在执行语句之前可以不用对函数声明8.5
函数参数及其传递方式形参与实参形式参数:定义函数时函数名后面括号中的变量名实际参数:调用函数时函数名后面括号中的表达式c=max(a,b);(main函数)(max函数)max(intx,inty){intz;z=x>y?x:y;return(z);}例:比较两个数并输出大者main(){inta,b,c;scanf("%d,%d",&a,&b);c=max(a,b);printf("Maxis%d",c);}max(intx,inty){intz;z=x>y?x:y;return(z);}形参实参说明:实参必须有确定的值形参必须指定类型形参与实参类型一致,个数相同若形参与实参类型不一致,自动按形参类型转换形参在函数被调用前不占内存;函数调用时为形参分配内存;调用结束,内存释放实参对形参参数传递只与位置有关,与参数名字无关。单向传递8.5
函数参数及其传递方式形参与实参形式参数:定义函数时函数名后面括号中的变量名实际参数:调用函数时函数名后面括号中的表达式例:计算x的立方#include<stdio.h>floatcube(floatx){return(x*x*x);}main(){floata,product;printf("Pleaseinputvalueofa:");scanf("%f",&a);product=cube(a);printf(”Cubeof%.4fis%.4f\n",a,product);}xaproduct××××1.21.21.728参数传递方式值传递方式方式:函数调用时,为形参分配单元并将实参的值复制到形参中;调用结束时,形参单元被释放,实参单元仍保留并维持原值特点:形参与实参占用不同的内存单元实参到形参是单向传递,形参不能传给实参。711x:y:调用前:调用结束:711x:y:例:交换两个数#include<stdio.h>main(){intx=7,y=11;printf("x=%d,\ty=%d\n",x,y);printf("swapped:\n");
swap(x,y);printf("x=%d,\ty=%d\n",x,y);}swap(inta,intb){inttemp;temp=a;a=b;b=temp;}调用:711a:b:711x:y:swap:711x:y:117a:b:temp例:求两个数中的最大数intmax(inta,intb){intc;if(a>b)c=a;elsec=b;return(c);}main(){intx,y,z;scanf("%d%d",&x,&y);z=max(x,y);printf("x=%d,y=%d,max=%d\n",x,y,z);}调用函数时才为形参分配存储单元,同时将实参的值传递给形参从键盘上输入35,则程序的运行过程如下:a:b:c:未定义未定义未定义x:y:z:???35?5x=3,y=5,max=5函数表达式,函数中需要return语句35未定义未定义未定义未定义未定义未定义355例:写一个求圆面积的函数,并在主函数中调用它floatarea(intr)return(3.1415926*r*r);floats;s=3.1415926*r*r;returns;main()floatr,s;scanf("%f",&r);s=area(r);printf("r=%fs=%f\n",r,s);intr;floats;scanf("%d",&r);在不同的函数中可以使用同名变量,但它们并不代表同一对象例:写一个函数,判断任一整数m是否为素数,程序1:prime(intm){intj;for(j=2;j<=m-1;j++)if(m%j==0)break;if(j>=m)else}main(){intn;scanf("%d",&n);
}printf("%disaprimenumber\n",);printf("%disnotaprimenumber\n",);prime();mmn例:写一个函数,判断任一整数m是否为素数?程序2prime(intm){intj,flag;for(j=2;j<=m-1;j++)if(m%j==0)break;if(j>=m)else}main(){intn;scanf("%d",&n);
}flag=1;flag=0;return();if(flag==1)printf("%disaprimenumber\n",);elseprintf("%disnotaprimenumber\n",);nnflagprime(n)prime(n)!=0prime(n)地址传递方式方式:函数调用时,将数据的存储地址作为参数传递给形参特点:实参和地址常量或变量、形参必须是地址变量形参与实参仍占用不同的单元,但存储内容一样,都是同一地址。
尽管实参到形参间仍然是“单向传递”,但若在函数中改变形参存储的地址所指向的内容,和在主程序中的效果是一样的,因为是同一个地址上的内容发生变化。
swap(int*p1,int*p2){intp;p=*p1;*p1=*p2;*p2=p;}main(){inta,b;scanf("%d,%d",&a,&b);printf(“a=%d,b=%d\n”,a,b);printf(“swapped:\n”);swap(&a,&b);printf(”a=%d,b=%d\n",a,b);}例:交换两个数(函数参数为地址,指针部分讲)a59b调用前:a59b调swap:p1&a&bp2a95b交换:p1&a&bp2a95b返回:#include<stdio.h>
longsum(inta,intb);longfactorial(intn);main(){intn1,n2;longa;scanf("%d,%d",&n1,&n2);a=sum(n1,n2);printf("a=%1d",a);}longsum(inta,intb){longc1,c2;c1=factorial(a);c2=factorial(b);
return(c1+c2);}
longfactorial(intn){longrtn=1;inti;for(i=1;i<=n;i++)rtn*=i;
return(rtn);}longsum(inta,intb);longfactorial(intn);文件包含编译预处理命令函数类型说明函数定义函数调用函数调用函数返回值形参实参8.6
函数的嵌套与递归调用嵌套调用:在调用一个函数的过程中又调用了另外一个函数,叫作函数的嵌套调用。嵌套调用的执行过程:从什么地方调用函数,就返回到什么地方。main()调用函数a结束a函数b函数调用函数b
C规定:函数定义不可嵌套,但可以嵌套调用函数递归调用定义:函数直接或间接的调用自身叫函数的递归调用递归调用应注意的问题:要防止无限次的递归调用。尽管C编译系统对递归函数的自调用次数没有限制,但每调用函数一次,在内存堆栈区分配空间,用于存放函数变量、返回值等信息。若递归次数过多,可能引起堆栈溢出。防止无限递归调用的方法:使用if语句设置一个递归过程结束的条件,且在递归过程中使该条件逐步趋于成立例:有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人多大。
age(5)=age(4)+2age(4)=age(3)+2age(3)=age(2)+2age(2)=age(1)+2age(1)=10用数学公式表述如下:age(n)=
age(n-1)+2(n>1)age(n)=10(n=1)递归求解两个阶段:第一阶段回推、第二阶段递推。回推结束的终止条件:第1个人10岁
age(int
n){intc;if(n==1)c=10;elsec=age(n-1)+2;returnc;}main(){printf("%d",age(5));}递归过程跟踪分析:P172图8.11P173图8.12age(5)c=age(4)+2c=age(3)+2c=age(2)+2c=age(1)+2c=10
(age(1))c=12
(age(2))c=14
(age(3))c=16
(age(4))c=18
(age(5))返回main函数递归算法解决问题的方式:将初始问题转化为解决方法相同的新问题,而新问题的规模要比原始问题小;新问题又可以转化为规模更小的问题,这样处理问题直至最终归结到可以简单解决的问题——递归的终结条件。例:求n的阶乘#include<stdio.h>intfac(intn){intf;if(n<0)printf("n<0,dataerror!");elseif(n==0||n==1)f=1;elsef=fac(n-1)*n;return(f);}main(){intn,y;printf("Inputaintegernumber:");scanf("%d",&n);
y=fac(n);printf("%d!=%15d",n,y);}递归的终结条件:n=0或n=1求n!也可以用递归方法,即5!等于5×4!,而4!=4×3!…1!=1。其它可以使用递归方法解决的问题Fabonacci数列:(P125迭代的方法,P133一维数组的方法)longfab(intn){longintt;if(n==1||n==2)t=1;elset=f(n-1)+f(n-2);return(t);}
猴子吃桃子问题
其它可以使用递归方法解决的问题8.7
数组作为函数参数8.7.1数组元素作函数实参——值传递数组元素可以作为函数的实参,与用变量作实参一样,是单向传递,即“值传送”方式。但形参不能为数组元素。例:两个数组大小比较:假定a[10]和b[10]为有10个元素的整型数组,比较规则如下。432105a562312107688432105b212343986654n=0m=0k=0in=0m=0k=1in=0m=1k=1in=1m=1k=1in=1m=1k=2in=2m=1k=2in=3m=1k=2比较两数组对应元素变量n,m,k记录a[i]>b[i],a[i]==b[i],a[i]<b[i]的个数最后若n>k,认为数组a>b
若n<k,认为数组a<b
若n==k,认为数组a==b#include<stdio.h>main(){inta[10],b[10],i,n=0,m=0,k=0;printf("Enterarraya:\n");for(i=0;i<10;i++) scanf("%d",&a[i]);printf("Enterarrayb:\n");for(i=0;i<10;i++) scanf("%d",&b[i]);for(i=0;i<10;i++){if(large(a[i],b[i])==1)n=n+1;
else
if(large(a[i],b[i])==0)m=m+1;
elsek=k+1;}}intlarge(intx,inty){intflag;if(x>y)flag=1;elseif(x<y)flag=-1;elseflag=0;return(flag);}函数的功能是判断形参x,y的大小,返回1,0,-1三种函数值数组元素a[i],b[i]作实参数组名作函数参数地址传递形参和实参必须均为数组名,且类型应一致形参数组大小可不指定,C语言编译对形参数组大小不做检查,只是将实参数组的首元素的地址传给形参数组形参数组名是地址变量,它获得了实参数组的首元素的地址
8.7.2数组名作函数参数
用数组名作函数参数时,此时形参应当用数组名或用指针变量。例写一函数求10个学生的平均成绩#include<stdio.h>
floataverage(intstu[10],intn);voidmain(){intscore[10],i;floatav;printf("Input10scores:\n");for(i=0;i<10;i++)scanf("%d",&score[i]);av=average(score,10);printf("Averageis:%.2f",av);}floataverage(int
stu[10],intn){inti;floatav,total=0;for(i=0;i<n;i++)total+=stu[i];av=total/n;returnav;}数组做实参,仅用数组名形参用数组定义,intstu[]..2109score562312….….88stu调用时,将数组的首地址传递给形参,形参数组与实参数组共用同一段存储单元,相当于同一个数组说明:1、用数组名做函数参数时,应该在主调函数和被调函数中分别定义数组。如score是实参数组名,stu是形参数组名。2、实参数组和形参数组类型必须一致(现都为int)。3、调用时,将实参数组的首地址传递给形参,这样形参数组首元素地址stu[0]和实参数组首元素score[0]具有同一地址,占用同一存储单元,其他元素也是,可理解为形参数组与实参数组共用同一段存储单元,相当于同一个数组。4、指定形参数组长度不起任何作用,故形参数组的长度10可省略,直接在形参数组名后跟一个空的[]。原因:C语言编译对形参数组大小不做检查,只是将实参数组的首元素的地址传递给形参数组。5、为方便被调用函数中处理数组元素的需要,可以另设一个形参,传递需要处理的数组元素的个数。如
aver=average(score,10);aver=average(score,5);例数组元素与数组名作函数参数比较12a调用前a[0]a[1]12a调用a[0]a[1]12xy21xy交换12a返回#include<stdio.h>voidswap2(intx,inty){intz;z=x;x=y;y=z;}main(){inta[2]={1,2};
swap2(a[0],a[1]);printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);}值传递12a调用前12ax调用21ax交换21a返回#include<stdio.h>voidswap2(intx[]){intz;z=x[0];x[0]=x[1];x[1]=z;}main(){inta[2]={1,2};
swap2(a);printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);}地址传递例数组元素与数组名作函数参数比较实质是实参数组和形参数组共享同一段内存单元
数组名作函数实参时,传递的是实参数组首元素的地址。这样两个数组共占同一段内存单元。形参数组中各元素的值如发生变化会使实参数组元素的值同时引起变化,上图很容易看出。这一点是与数组元素以及变量做函数参数的情况不同的,务请注意。
关于值调用和地址调用总结值调用。指的是传送给形参的是值。这时,即使主程序中的变量和函数中的变量同名,但它们不在同一个地址上,函数中变量的值发生改变,并不会改变主程序中变量的值。当从函数返回到主程序后,函数中变量的值被释放(静态变量除外),这时,在主程序中看不到在函数中被改变的值(静态变量除外),即所谓的“单向传递”。
(2)地址调用。指的是传送给形参的是地址。这时函数中的变量和主程序中的变量是在同一个地址上(与变量是否同名无关),同一个地址上的内容发生变化,在函数中和在主程序中的效果是一样的,即所谓的“双向传递”。
例:用选择排序法对数组中10个整数按由小到大排序选择法对n个数进行排序的思路是:如果要对n个数据排序,就需要进行n-1轮的比较,每次都从数组中未排好的子序列中找出一个最小数,与子序列最前面的1个元素交换,使小数放在子序列的最前面。例如,第1轮需要比较n-1次,在n个数中找出最小数与数组中的第1个元素(即a[0])交换,使数组中的最小数放在数组的最前面;第2轮将对剩下的n-1个数进行比较,需要比较(n-1)-1次,将n-1个数中的最小的数与子序列最前面的元素交换,也就数组中的第2个元素(即a[1])交换,使子序列中的最小数放在子序列的最前面;……以此类推,第n-1轮需要进行1次比较,将剩下的2个数中的小数与子序列的前面即a[n-2]交换,使子序列中的最小数子序列中的前面。当第n-1轮比较进行完后,所有的数据都按照升序在数组中排列。例:以6个数为例说明选择法的步骤(从小到大)选择排序:
第1轮比较时,用a[0]依次与a[1]到a[5]进行比较,如果a[0]较大则进行交换,否则不变。第1轮结束后,a[0]中为最小数.
第2轮比较时,用a[1]依次与a[2]到a[5]进行比较,如果a[1]较大则进行交换,否则不变。第2轮结束后,a[1]中为次小数.
以后各轮比较过程类似.972541a[0]a[1]a[2]a[3]a[4]a[5]
19754212975479574524
124975
1245
9
7795745972541729712初始状态第1轮第2轮第3轮第4轮第5轮795779选择排序法第二种方法(从小到大)第二种方法:
两两比较后并不马上交换,而是找到最小数后记下其下标P。在一轮比较完毕后,再将最小的数一次交换到位。——比较次数不变,交换次数减少。972541a[0]a[1]a[2]a[3]a[4]a[5]12457912457997254191初始状态第1轮第2轮第3轮第4轮第5轮p=0p=1p=2p=5
172549p=1p=227
1
27549p=247p=3p=4p=3p=4选择排序法第二种方法(从小到大)972541a[0]a[1]a[2]a[3]a[4]a[5]12457912457997254191初始状态第1轮第2轮第3轮第4轮第5轮p=0p=1p=2p=5
172549p=1p=227
1
27549p=247p=3p=4p=3p=4第1轮i=0{p=0;for(j=i+1;j<6;j++)if(a[j]<a[p])p=j;//比较5次,
if(p!=i){a[p]a[0]}}第2轮i=1{p=1;for(j=i+1;j<6;j++)if(a[j]<a[p])p=j;//比较4次,
if(p!=i){a[p]a[1]}}第5轮i=4{p=4;for(j=i+1;j<6;j++)if(a[j]<a[p])p=j;//比较1次
if(p!=i){a[p]a[4]}}main(){inta[6],i,j,p,temp;for(i=0;i<6;i++)scanf("%d",&a[i]);for(i=0;i<5;i++){p=i;for(j=i+1;j<6;j++)if(a[j]<a[p])p=j;if(p!=i){temp=a[p];a[p]=a[i];a[i]=temp;}}for(i=0;i<6;i++)printf("%3d",a[i]);}1、第1轮排序找到所有元素的最小值a[p],然后将a[0]与a[p]交换,则第1轮结束后,a[0]中为最小数。第1轮找最小值a[p]的方法:令p=0,扫描a[1]…a[5],有小于a[0]的即令p记录其下标,否则p不变。之后比较a[p]与剩余元素,若有比a[p]小的,则改变p值,否则p不变。该轮比较结束时,p记录的就是最小值的下标。2、其他几轮与此类似,只是不要再动已排好的元素.例:用选择排序法对数组中10个整数按由小到大排序voidsort(intarray[],intn){inti,j,k,t;
for(i=0;i<n-1;i++){k=i;
for(j=i+1;j<n;j++) if(array[j]<array[k])k=j; if(k!=i) {t=array[i]; array[i]=array[k]; array[k]=t; }}}main(){inta[10],i;for(i=0;i<10;i++) scanf("%d",&a[i]);
sort(a,10);for(i=0;i<10;i++)printf("%d",a[i]);printf("\n");}0123456789a4968573299927137688arraykjjjkjkjjjjj949i=0例:用选择排序法对数组中10个整数按由小到大排序voidsort(intarray[],intn){inti,j,k,t;
for(i=0;i<n-1;i++){k=i;
for(j=i+1;j<n;j++) if(array[j]<array[k])k=j; if(k!=i) {t=array[i]; array[i]=array[k]; array[k]=t; }}}main(){inta[10],i;for(i=0;i<10;i++) scanf("%d",&a[i]);
sort(a,10);for(i=0;i<10;i++)printf("%d",a[i]);printf("\n");}kjjkjkjjjjj0123456789a4968573299927137688array949kk1368i=10123456789a9132732495768768899arrayi=8例:用选择排序法对数组中10个整数按由小到大排序voidsort(intarray[],intn){inti,j,k,t;
for(i=0;i<n-1;i++){k=i;
for(j=i+1;j<n;j++) if(array[j]<array[k])k=j; if(k!=i) {t=array[i]; array[i]=array[k]; array[k]=t; }}}main(){inta[10],i;for(i=0;i<10;i++) scanf("%d",&a[i]);
sort(a,10);for(i=0;i<10;i++)printf("%d",a[i]);printf("\n");}例:求二维数组中最大元素值(多维数组名作为函数参数)1357246815173412ijmax=11357246815173412ijmax=31357246815173412ijmax=5j1357246815173412imax=7j1357246815173412imax=7j1357246815173412imax=34intmax_value(intarray[3][4]){inti,j,k,max;max=array[0][0];for(i=0;i<3;i++)for(j=0;j<4;j++) if(array[i][j]>max) max=array[i][j];return(max);}main(){inta[3][4]={{1,3,5,7}, {2,4,6,8},{15,17,34,12}};printf("maxvalueis%d\n",max_value(a));}多维形参数组第一维维数可省略,第二维必须相同
intarray[][4]8.8局部变量和全局变量变量按照其定义位置分为局部变量和全局变量。局部变量只能在一个函数内或者一个复合语句内使用。全局变量在定义之后的所有的函数都能使用。变量的分类按数据类型字符型变量数值型变量整型变量实型变量长整型变量基本整型变量无符号整型变量单精度型变量双精度型变量按定义位置外部变量(外部变量)内部变量(局部变量)按存储类别动态存储变量静态存储变量一、局部变量(按定义位置)局部变量:只在某一个函数内部或者只在一个复合语句内起作用的变量叫做局部变量。凡定义在函数内部的变量、定义在复合语句内部的变量、函数的形式参数均为局部变量.说明:(1)局部变量作用域(可见性):本函数(或复合语句)内也就是说只有在本函数(或复合语句)内才能使用它们,在此函数以外是看不到它们,不能使用这些变量的。(2)不同函数中可以使用相同名字的局部变量,他们在内存中占不同的存储空间,互不混淆。floatf1(inta){intb,c;…….}charf2(intx,inty){inti,j;……}main(){intm,n;…….}a,b,c有效x,y,i,j有效m,n也是局部变量,仅在main()中有效main(){inta,b;a=3;b=4;printf("main:a=%d,b=%d\n",a,b);sub();printf("main:a=%d,b=%d\n",a,b);}sub(){inta,b;a=6;b=7;printf("sub:a=%d,b=%d\n",a,b);}运行结果:main:a=3,b=4sub:a=6,b=7main:a=3,b=4例:局部变量作用域例:不同函数中同名变量二、全局变量(按定义位置)全局变量:定义在函数以外,可以为整个源程序文件中各个函数使用的变量叫做全局变量。凡定义在所有函数之外的变量均为全局变量.说明:(1)外部变量的作用域(可见性):从定义点开始,或有extern声明的位置开始到文件结束,全局可见。(2)局部变量与全局变量可以同名,他们在内存中
分别占有不同的存储单元,且在局部变量起作
用的范围内,全局变量不起作用。(3)全局变量增加了函数间的数据联系,但降低了
函数的通用性,应尽量少用。当需要由函数传
回多个值时才考虑使用全局变量。例:全局变量作用域intp=1,q=5;floatf1(inta){intb,c;…….}intf3(){…..}charc1,c2;charf2(intx,inty){inti,j;……}main(){intm,n;…….}c1,c2的作用范围p,q的作用范围inta=3,b=5;max(inta,intb){intc;c=a>b?a:b;return(c);}main(){inta=8;printf("max=%d",max(a,b));}例:全局变量与局部变量运行结果:max=8
定义在所有函数之外的变量为全局变量形参变量为局部变量定义在函数内部的变量为局部变量实参a是局部变量,值为8,实参变量b是全局变量,其值为5。a,b均使用局部变量的值注意:
1、3次出现了a的定义,3处a的存储空间不同
2、局部变量起作用时,全局变量不起作用说明:(1)引入全局变量max、min,有效范围从定义变量的位置开始到本源文件结束。(2)可增加函数之间传递数据的途径。在函数调用时实现返回两个或两个以上的计算结果。
floatmax,min;floataverage(floatarray[],intn){inti;floatsum=array[0];max=min=array[0];for(i=1;i<n;i++){if(array[i]>max)max=array[i];elseif(array[i]<min)min=array[i];sum+=array[i];}return(sum/n);}main(){inti;floatave,score[10];/*Input*/ave=average(score,10);printf("max=%6.2f\nmin=%6.2f\naverage=%6.2f\n",max,min,ave);}作用域maxmin例:全局变量可增加函数间传递数据的途径,但降低函数通用性。说明:(3)设置全局变量是双刃剑,非必要时勿使用全局变量,原因:全局变量在程序全部执行过程中占用存储单元,而不是在需要时再开辟,浪费存储空间。例:全局变量可增加函数间传递数据的途径,但降低函数通用性。看出:aver通过return带回main函数。max和min是全局变量,是公用的,它们的值可以供各函数使用,如在某个函数中,改变它们的值,在其它函数中也可以使用这个已改变了的值。
降低了函数的通用性、可靠性,可移植性。牵一发而动全身
故一般还是采用“实参——形参”的渠道和外界联系。§8.9变量的存储类别变量的存储类别:按时间域(生存期)来进行分类。按生存期,变量可分为:静态存储变量和动态存储变量。一、内存中用户区存储空间的分配内存系统区用户区存放系统程序存放用户文件和数据程序区:静态存储区:动态存储区:程序全局变量静态局部变量(用static声明的变量)局部自动变量(auto)函数的形参二、变量的存储类别静态存储:含义:定义变量时分配存储单元,函数调用结束时并不释放所占存储单元。离开函数后变量的值仍能被引用,变量的生存期是程序运行期间。静态存储区中存储:静态局部变量、全局变量。动态存储:含义:调用函数时分配存储单元,函数调用结束时释放所占存储单元。即离开函数后变量的值不能被引用,变量的生存期是函数运行期间。动态存储区中存储:自动局部变量、函数的形参。二、变量的存储类别每一个变量都有两种属性:数据类型和存储类别,前者表示了变量所能存放的数据类型和范围,后者表示了变量在内存中的存储方式。局部变量在定义时存储类别缺省时,默认为自动(auto)变量。当要指出该变量是静态局部变量时应加static。定义一个动态局部变量(自动变量):
autointa;
或者:inta;定义一个静态局部变量:
staticintb;动态局部变量定义后的初值是随机值静态变量定义后的初值是0三、局部变量的存储方式局部自动变量在动态存储区分配存储单元。局部静态变量在静态存储区分配存储单元。静态局部变量是在编译时赋初值的,只赋初值一次,下次调用函数时,这些变量保留上次调用结束时的值。
局部自动变量是在调用时赋初值,每调用一次,重新赋一次初值。#include<stdio.h>intf(inta){autointb=0;
intc=3;
b=b+1;
c=c+1;return(a+b+c);}voidmain(){
inta=2,i;for(i=0;i<3;i++)printf(“%d”,f(a));
}例:局部静态变量应用实例1,教材8.17#include<stdio.h>intf(inta){autointb=0;
staticintc=3;
b=b+1;
c=c+1;return(a+b+c);}voidmain(){inta=2,i;for(i=0;i<3;i++)printf(“%d”,f(a));
}结果:7,8,9b、c是自动变量,函数调用结束后,b、c的存储空间释放。再次调用f,b、c重新赋值。结果:7,7,7c为静态局部变量,程序运行期间存储空间不释放,只在编译时赋初值3,第2次以后再调f函数时,c中保留上次调用后的值。次数调用前c调用后c一34二45三56次数调用前c调用后c一34二34三34main(){voidincrement(void);increment();increment();increment();}voidincrement(void){intx=0;x++;printf(“%d\n”,x);}运行结果:111main(){voidincrement(void);increment();increment();increment();}voidincrement(void){staticintx=0;x++;printf(“%d\n”,x);}运行结果:123例:局部静态变量应用实例2例:局部静态变量应用实例3,教材8.18输出1到5的阶乘值。#include<stdio.h>voidmain(){intfac(intn);inti;
for(i=0;i<5;i++)printf(“%d!=%d\n”,i,fac(i));}intfac(intn){staticintf=1;
f=f*n;
return(f);
}四、全局变量的存储方式全局变量生存期:全局变量在编译时被分配在静态存储区全局变量的生存期是整个程序运行期间全局变量作用域:是在函数的外部定义的,它的作用域是从变量的定义处开始,到本程序文件的末尾。若对全局变量定义之外加extern声明可以扩展其作用范围对对全局变量定义之前加static,则将其作用范围局限在本文件。intp=1,q=5;floatf1(inta){intb,c;…….}intf3(){…..}charc1,c2;charf2(intx,inty){inti,j;……}main(){intm,n;…….}c1,c2的作用范围p,q的作用范围externc1,c2;externc1,c2;c1,c2的作用范围扩展后c1,c2的作用范围扩展后extern对全局变量作用域的扩展全局变量定义与全局变量声明不同名称定义方法定义位置存储类别生存期作用域动态局部变量静态局部变量全局变量函数形参autointa;inta;staticinta;inta;staticinta;inta;函数内部复合语句内函数内部复合语句内函数外部函数说明部分动态存储区静态存储区静态存储区动态存储区函数复合语句程序程序函数函数内复合语句内函数内复合语句内本程序和其它程序函数内变量存储类别小结局部变量默认为auto型register型变量个数受限,且不能为long,double,float型局部static变量具有全局寿命和局部可见性局部static变量具有可继承性extern不是变量定义,可扩展外部变量作用域变量存储类型静态动态存储方式程序整个运行期间函数调用开始至结束生存期编译时赋初值,只赋一次每次函数调用时赋初值自动赋初值0或空字符不确定未赋初值静态存储区动态区存储区寄存器局部变量全局变量作用域定义变量的函数或复合语句内本文件
其它文件register局部staticauto全局static全局存储类别例变量的寿命与可见性(自学)#include<stdio.h>inti=1;main(){staticinta;
registerintb=-10;
intc=0;printf("-----Main------\n");
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026广东广州市爱莎文华高中招聘备考题库带答案详解(新)
- 2026江苏苏州高新区实验初级中学招聘1人备考题库附答案详解(b卷)
- 2026安徽第二医学院高层次人才招聘20人备考题库参考答案详解
- 2026清华大学出版社校园招聘备考题库及答案详解(基础+提升)
- 2026内蒙古鄂尔多斯东胜区第一小学三部教师招聘1人备考题库附答案详解(基础题)
- 2026山东济南市妇幼保健院招聘卫生高级人才和博士(控制总量)26人备考题库【含答案详解】
- 2026黑龙江齐齐哈尔市拜泉县乡镇卫生院招聘医学相关专业毕业生5人备考题库及答案详解(夺冠)
- 2026江苏保险公司销售人员招聘备考题库参考答案详解
- 2026新疆喀什昆仑建设有限公司招聘3人备考题库及参考答案详解(黄金题型)
- 2026湖南湘潭医卫职业技术学院招聘5人备考题库附参考答案详解(精练)
- 锅炉外包托管合同范本
- 建筑工程行业“防汛、防台风、防雷电”三防安全培训
- 白内障术后护理注意事项
- 数字绘画笔触创新-洞察与解读
- 多联机空调运维、维保技术服务方案
- 农村美食旅游推广创新创业项目商业计划书
- 2025年中考数学试题及答案常州
- 一级实验室生物安全手册
- 预算授权管理暂行办法
- DB11∕T 1200-2023 超长大体积混凝土结构跳仓法技术规程
- 毕业设计(论文)-自动取药转运一体机结构设计
评论
0/150
提交评论