【文档说明】C语言课件教学-第8章-函数.ppt,共(95)页,179.547 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-44597.html
以下为本文档部分文字说明:
第8章函数8.1概述C语言是通过函数来实现模块化程序设计的。一个较大的程序应分为若干程序模块,每个模块实现一个特定的功能,这个模块称为子程序。C的子程序是通过函数实现的,函数是C语言程序的基本单位。函数的构成•C源程序是由函数组成的。函数
是C源程序的基本模块,通过对函数模块的调用实现特定的功能。•实用程序往往包括一个主函数main()和若干其他函数。其中主函数main()是必须的,它是所有函数的执行起点。由主函数调用其它函数,其它函数也可以互相调用,同一函数可以被一个或多个函
数调用任意多次。调用示意图如下:C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函数式语言。f11(){…}f11(){…}f31(){…}f1(){…f11();…}f2(){…f11();f22();…}main(){…f1();…f2();
…}程序开发中使用函数的优点1)使用函数可以控制任务的规模2)使用函数可以控制变量的作用范围3)使用函数,程序的开发可以由多人分工协作4)使用函数,可以重新利用已有的、调式好的、成熟的程序模块5)函数模块相对独立,功能单一,
可混合编写也可独立编写调试。函数的一些说明1)一个C程序由多个程序模块组成,每个模块作为一个源程序文件,多个源程序文件组成一个C程序,这样便于分别编写分别编译,提高调试效率,一个源程序文件可为多个C程序共
用。2)一个源程序文件由一个或多个函数及其相关内容(如数据定义等)组成,一个源程序文件是一个基本的编译单位。3)C程序的执行从主函数main()开始(称为主调函数),可以调用其它函数(称为被调用函数),调用流程返回mai
n(),最后函数在main()中结束。4)所有函数都是平行的,在定义时候是分别进行的,相互独立,无从属关系,不可嵌套定义。函数间可相互调用,但不能调用主函数,主函数只能由系统调用。函数的分类1.从用户使用的角度1)标准函数(库函数),由系统提供,用户不必自定义可直接使用,注意:不同
C编译系统提供的库函数可能有些不同2)用户自定义函数,用来解决用户专门需要。2.从函数的形式1)无参函数。在调用函数时,main不向被调用函数传递数据,只用来执行一组操作。2)有参函数,主调函数在调用被调用函数时,通过参数向其传递数据,一般情况下,执行被调用函数时,得到一个函数值,供主调函数使用。
8.2函数定义的一般形式函数应当先定义,后调用(1)无参函数的一般形式函数类型函数名(){说明语句部分;可执行语句部分;}无参函数一般不需要返回函数值,函数类型void类型(空类型)•2)有参函数的一般形式函数类型函数名(形参表列){说明语句部分;可执
行语句部分;}例:intmax(x,y)intx,y;/形式参数说明/{intz;/函数体中的说明部分/z=x>y?x:y;return(z);}这两行可以写成一行:intmax(intx,inty)3.“空函数”类型说
明符函数名(){}•“空函数”什么操作也不做。其作用是在此处留一函数的位置,以便将来扩充功能之用。函数名也在将来换取实际的函数名。函数定义的一些说明1.函数头(首部):说明了函数类型、函数名称及参数。(1)函数类型:函数返回值的数据类型,可以是基本数据类
型也可以是构造类型。如果省略默认为int,如果不返回值,定义为void类型。(2)函数名:给函数取的名字,以后用这个名字调用。函数名由用户命名,命名规则同标识符。(3)函数名后面是参数表,无参函数没有参
数传递,但“()”号不能省略,这是格式的规定。参数表说明参数的类型和形式参数的名称,各个形式参数用“,”分隔。2.函数体:函数首部下用一对{}括起来的部分。如果函数体内有多个{},最外层是函数体的范围。函数体一般包括声明部分、执行部分两部分。1)声明部分:在这部分
定义本函数所使用的变量和进行有关声明(如函数声明)。2)执行部分:程序段,由若干条语句组成(可以在其中调用其它函数)。例:输入三个整数,求三个整数中的最大值不使用函数(除main外)main(){intn1,n2,n3,nmax;scanf
(“%d%d%d”,&n1,&n2,&n3);if(n1>n2)nmax=n1;elsenmax=n2;if(n3>max)max=n3;printf(“max=%d\n”,nmax);}使用函数intmax(int,int,int)
;/*函数声明*/main(){intn1,n2,n3,nmax;scanf(“%d%d%d”,&n1,&n2,&n3);nmax=max(n1,n2,n3);printf(“max=%d\n“,nmax);}intmax(intx,inty,i
ntz){intm;if(x>y)m=x;elsem=y;if(z>m)m=z;returnm;}像调用库函数一样调用函数定义8.3函数参数和函数的值8.3.1形式参数与实际参数在调用函数时,大多情况下,主调与被调函数间有数据传递关
系,这就是有参函数。在定义函数时,函数名后面括号中的变量名称为“形式参数”,在主调函数中调用一个函数时,函数名后面括号中的参数(可以是表达式)称为“实际参数”。发生函数调用时,调用函数把实参的值复制一份,传送给被调用函数的形参,从而实现调用函数向被调
用函数的数据传送。例从键盘输入两个数,输出其中较大的一个。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=x>y?x:y;return(z);}说明:1)形参变量在被调用前不占用存储单元;在被调用结束后,形参所占存储单元亦被释放。因此,形参只有在该函数内有效。调用结束,返回调用函数后,则不能再使用该形参
变量。2)实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此,应预先用赋值、输入等办法,使实参获得确定的值。3)实参对形参的数据传送是单向的,即只能把实参的值传送给形参,而不能把形参的值反
向地传送给实参。4)实参和形参占用不同的内存单元,即使同名也互不影响5)在被定义函数中,必须指定形参的类型。实参和形参的类型应相同或赋值相容。•main()•{inta=3,b=5;•voidswap(int,i
nt);•swap(a,b);•printf(“a=%d,b=%d\n”,a,b);•}•voidswap(intx,inty)•{inttemp;•temp=x;x=y;y=temp;•printf(“x=%d,y=%d\n”,x,y);•}•是按值传递的•按
址传递放在指针里面讲。传递值35ab35xy3tempMain()函数:调用Swap函数8.3.2函数的返回值通常,希望通过函数调用使主调函数得到一个确定的值,这就是函数的返回值。说明如下:1)函数的返回值是通过retur
n语句获得的。当不需返回函数值时,可省去return语句。2)return语句的后面可以有括号,也可以没有。如:returnz;return(z);3)return语句的后面可以是变量,也可以是表达式。如:return(x>y?x:y);4)return语句返回值的类型应与该函数的类
型一致。否则以函数类型为准。5)若函数中没有return语句,则该函数被调用后也会带回不确定的值。为了明确表示不需要函数返回值,可以用“void”定义函数为“无类型”。凡不需要返回值的函数,一般均定义为“
void”类型。6)一个函数可以有一个以上的return语句,执行到哪个return语句,哪个return语句起作用。8.4函数的调用在程序中,是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似。C语言中,函数调用的一般形式为:函数名(实际参数表)说明:1)
对于无参函数,尽管没有“实参表”,但也不得省略括号。2)“实参表”中的参数之间用逗号分开。3)实参与形参之间的个数及类型必须一一对应。4)对实参求值的顺序是自左至右还是自右至左,视具体的系统而定。TurboC和MSC是按自右至左的顺序求值。见P162例8.48.4.2函数调用的方式按照函
数在程序中出现的位置,可以有以下三种调用方式:1)函数语句:C语言中的函数可以只进行某些操作而不返回函数值,这时的函数调用可作为一条独立的语句。如printf(“Cpragram”)gets(s);2)函数表达式:函数作为表达式的一项,出现在表达式中,以函数返回值参与表达式的运算。这种方式要求
函数是有返回值的。如:c=2max(a,b);3)函数参数:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。如:m=max(max(a,b),c);8.4.3对被调用函数的声明和函数原型对被调用函数的声明在一个函数被另一个函数调用
时,须具备以下条件:1)被调用的函数已存在2)如果被调函数为库函数,则应在文件开头用“#include”命令声明相应的“头文件”。如:#include“stdio.h”#include“math.h”3)如果被调函数为自定义函数且
其定义在主调函数定义之后,则应在主调函数中说明其类型(即对被调用函数进行声明)。函数声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在调用时,编译系统能正确识别函数并检查调用是否合法。函数声明又称函数原型。格式如下:函数类型函数名(参数类型1,参数类型2,…,参数类型n);函
数类型函数名(参数类型1,参数名1,参数类型2,参数名2,…,参数类型n,参数名n);如果不声明,系统无法在调用时对函数调用的正确性进行检查,容易出错。注意:函数的定义和声明不是一回事。定义是对函数功能的确立,包括指定函数名、函数值
类型、形参及其类型、函数体等,它是一个完整的、独立的的函数单位。而函数的声明则是把函数的名字、函数类型以及形参的类型、个数和顺序等通知编译系统以便在调用时进行对照检查。如:main(){…doublenew_style(int,double);/
*函数声明*/…}Doublenew_style(inta,doublex)/*函数定义*/{…}说明:1)类型标识符被调函数名();这种声明形式也是合法的,但不提倡。2)如果被调函数为自定义函数且其定义在主调函数定义之前,则在主调函数中可不必说明其类型。因为编译程序已知道其类型。3)如果
被调函数的值是整型或字符型,可不必声明类型,系统自动按整型说明。4)如果在所有被调函数定义之前、在文件的开头、在函数的外部已对被调函数作了类型说明,则在各主调函数中可不必说明其类型。见P166例。charletter();floatf();
inti();main(){}一开始就将所有要被调用的函数作出声明/主调函数中不必说明它所调用的函数的类型/8.5函数的嵌套调用•函数不允许嵌套定义,但是允许嵌套调用。函数的嵌套调用是指,在执行被调用函数时,被调
用函数又调用了其它函数。这与其它语言的子程序嵌套调用的情形是类似的,其关系可表示如下图:例:用弦截法求方程的根。x3–5x2+16x–80=0(1)取两个不同点x1、x2,如果f(x1)和f(x2)符号相反,则(x1,x2)区间内必有一个根。如果f(x1)与f(x2)同符号,则
应改变x1、x2,直到f(x1)、f(x2)异号为止。注意x1、x2的值不应差太大,以保证(x1,x2)区间只有一根。方法如下:(2)连接f(x1)和f(x2)两点,此线(即弦)交x轴于x.x点坐标可用下式求出:)()()()(121221xfxfx
fxxfxx再从x求出f(x)。xyxf(x1)f(x)x1x2f(x2)3)若f(x)与f(x1)同符号,则根必在(x,x2)区间内,此时将x作为新的x1。如果f(x)与f(x2)同符号,则表示根在(x1,x2)区间内,将x作为新的x2.
4)重复步骤(2)和(3),直到|f(x)|<为止,为一个很小的数,例如10-6。此时认为f(x)≈0.输入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)|<root=x输出root真假根据上述思路画出n-s流程图,见下图root函数程序由若干个函数构成,#include"math.h"floatf(x)/*定义f函数,以实现f(x)=x3-5x2+16x-80*/f
loatx;{floaty;y=((x-5.0)*x+16.0)*x-80.0;return(y);}floatxpoint(x1,x2)/*定义xpoint函数,求出弦与x轴交点。*/floatx1,x2;{floaty;y=(x18f(x2)-x2*f(x1)/(f(x
2)-f(x1);return(y);}floatroot(x1,x2);/*定义root函数,求近似根。*/floatx1,x2;{inti;floatx,y,y1;y1=f(x1);do{x=xpoint(x1,x2);y=f(x
);if(y*y1>0)/*f(x)与f(x1)同符号。*/{y1=y;x1=x;}elsex2=x;}while(fabs(y)>=0.0001);return(x);}main()/*主函数*/{floatx1
,x2,f1,f2,x;do{printf("inputx1,x2:\n");scanf(%f,%f,&x1,&x2);f1=f(x1);f2=f(x2);}while(f1*f2>=0);x=root(x1,x
2);printf("Arootofequationis%8.4f",x);}运行情况如下:inputx1,x2:2,6Arootofequationis5.0008.6函数的递归调用•在调用一个函数的过程中又
出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。直接调用intf(x)intx;{inty,z;z=f(y);}间接调用intf1(x)intx;{inty,z;z=f2(y);}intf2(t)intt;{inta,b;a=f1(y);
}显然:上述例子会无限递归(无限执行)。所以,在递归调用时都有条件限制。即:条件成立,调用递归,否则结束。例8.7有5人排成一队,从最后一人开始,其年龄均比前面的人大2岁,而最前面的人年龄是10岁,问最后一人的年龄是多少岁?main()age(5)age(4)+2age(n)n=5a
ge(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输出ag
e(5)age(n)intn;{intc;if(n==1)c=10;elsec=age(n–1)+2;return(c);}main(){printf(“%d\n”,age(5));}运行结果:18例8.8用递归方法
求n!1.从数学上定义n!=1(n=0,1)n(n–1)!(n>1)2.程序floatfac(n)intn;{floatf;if(n<0)printf("inputerror!\n");elseif(n==0¦¦n=
=1)f=1;elsef=nfac(n–1);return(f);}main(){intn;floaty;printf("inputainteger!")scanf("%d",&n);y=fac(n);p
rintf("%d!=%15.of",n,y);}3.执行过程:设输入n5main(){f=5fac(4);}fac(4){f=4fac(3);returnf;}fac(3){f=3fac(2);returnf;}–fac(2){f=2fac(1);re
turnf;}fac(1){f=f(1);returnf;}可简化表示为:当变成机器代码时,将其拉成直线(线性程序代码)。例:P118汉诺塔(Hanoi)问题BCAn个盘子问题:将A塔上n个盘子移至C(借助于B)。移动时,保证三个塔始终是大盘在
下,小盘在上。必须用递归方式解决,分析见P1751)先将A塔n–1个盘子借助于C移至B上2)将A上剩下的一个移至C上.3)将B上n–1个盘子借助于A移至C上.可以看到:1)、3)为同一问题,都为n–1个盘子借助于一个空塔移至另一塔上。程序如下:void
move(getone,putone)chargetone,putone;{printf("%c––>%c\n",getone,putone);}voidhanoi(n,one,two,three)/*将n个盘从one借助two,移动three*/charone,
two,three;intn;{if(n==1)move(one,three);else{hanoi(n–1,one,three,two);move(one,three);hanoi(n–1,two,one,three);}}main(){intm;prin
tf("inputthenumberofdisdes":);scanf("%d",&m);printf("Thesteptomoving%3ddisdes:\n",m);hanoi(m,'A','B','C'
);}运行情况如下:inputthenumberofdisdes:3Thesteptomoving3diskes:A>CA>BC>BA>CB>AB>CA>C两个函数:move(getone,
putone)表示从getone塔移一个盘子至putone塔hanoi(n,one,two,three)表示n个盘子从one塔借助于two塔(空)移至three塔。调用时塔用字符常量'A','B','C'表示。8.7数组作为函数参数分为两种情况:1.数组元素作
为实参2.数组名同时为形、实参一、数组元素作为实参由于数组元素与相同类型的简单变量地位完全一样;因此,数组元素作函数参数也和简单变量一样,也是值的单向传递例:设有两个同样大小的一维数组,a[10],b[10]将相应元素比较,统计a中大于b中对
应元素的个数,小于的个数,相等时的个数。程序如下:main(){intlange(intx,inty)inta[10],b[10],i,n=0,m=0,k=0;printf("enterarraya:\n");for(i=0
;i<10;I++)scanf(5,"d",&a[i]);printf("\n");printf("enterarrayb:\n");for(i=0;i<10;i++)scanf("%d",&b[i]);printf("\n");for(i=0;i<10;i++)
{if(large(a[i],b[i])==1)n=n+1;elseif(large(a[i],b[i])==0)m=m+1;elsek=k+1;}printf("a[i]>b[i]%dtimes\na[i]=b[i]%dt
imes\na[i]<b[i]%dtimes\n",n,m,k);if(n>k)printf("arrayaislargerthanarrayb\n");elseif(n<k)printf("arrayaissmallerthanarrayb\n");elseprintf
("arrayaisequaltoarrayb\n");}intlarge(x,y)intx,y;{intflag;if(x>y)flag=1;elseif(x>y)flag=–1;elseflag=0;return(flag);}运行情况如下:enterarraya:13579
86420enterarrayb:5389–1–35604a[i]>b[i]4timesa[i]=b[i]1timesa[i]<b[i]5timesarrayaissmallerthanarrayb二、数组名作实、形参特点
:直接用数组名作参数时,则为地址传送(不是值传送),即实参数组的首地址传递给形参数组首地址。所以,实参、形参数组共享相同的内存单元。1.形参数组可不指定大小,也可用另一参数作大小,以确定使用实数组的元素个数
。2.形参、实参数组必须类型一致。3.多维数组方式一样,仅第一维大小的说明可省略例1.有一个一维数组score,内放10个学生成绩,求平均成绩。程序如下:floataverage(array)floatarray[10];{inti;floataver,sum=
array[0];for(i=1;i<10;i++)sum=sum+array[i];aver=sum/10;return(aver);}main(){floatscore[10],aver;inti;printf("inpu
t10score:\n");for(i=0;i<10;i++)scanf("%f",&score[i]);printf("\n");aver=average(score);printf("averagescoreis%5.2f",aver);}运行情况如下
:input10scores:100567898.576879967.57597averagescoreis83.40程序2.floataverage(array,n)intn;floatarray[];{inti;float
aver,sum=array[0];for(i=1;i<n;i++)sum=sum+array[i];aver=sun/n;return(aver);}main(){staticfloatscore_1[5]={9
8.5,97,91.5,60,55}staticfloatscore_2[10]={67.5,89.5,99,69.5,77,89.5,76.5,54,60,99.5}printf(“theaverageofclassAis%6.2f\n”,average(score_1,5)
);printf(“theaverageofclassBis%6.2f\n”,average(score_2,10));}运行结果如下:theaverageofclassAis80.40theaverageofclassBis78.208.8局部变量和全局变量•8.8.1局部变量•概念:
是指在一定范围内有效的变量。C语言中,在以下各位置定义的变量均属于局部变量。•在函数体内定义的变量,在本函数范围内有效,作用域局限于函数体内。•在复合语句内定义的变量,在本复合语句范围内有效,作用域局限于复合语句内。•有参函数的形
式参数也是局部变量,只在其所在的函数范围内有效。•关于局部变量的作用域还要说明以下几点:•1.主函数main()中定义的内部变量,也只能在主函数中使用,其它函数不能使用。同时,主函数中也不能使用其它函数中定义的内部变量。因为主函数也是一个函数,与其它函数是平行关系。这一点是与其它语
言不同的,应予以注意。•2.允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。•例如:•intf1(inta)/*函数f1*/•{intb,c;•……•}/*a,b,c作用域:仅限于函数f1()中*/••intf2(intx)/*函数f2*/•{in
ty,z;•……•}/*x,y,z作用域:仅限于函数f2()中.x,y,z换为a,b,c也可以*/••main()•{intm,n;•……•}/*m,n作用域:仅限于函数main()中*/•8.8.2全局变量•全局变量:在函数之外定
义的变量。(所有函数前,各个函数之间,所有函数后)•全局变量作用域:从定义全局变量的位置起到本源程序结束为止。•全局变量可被作用域内的所有函数直接引用。在函数fun()中,虽然没有定义变量a,b,但由于它们定义在程序的最前面,是全程
变量,凡是在定义该变量的后面定义的函数均可以引用它们。•#include“stdio.h”•inta=3,b=5;/*在函数体外定义的变量*/•Main()•{voidfun();•printf(“a=%d
,b=%d\n”,a,b);•fun();•printf(“a=%d,b=%d\n”,a,b);•}•voidfun(void)•{•intc;•c=a;•a=b;•b=c;•}•说明:1.全局变量增加了
函数间数据联系的渠道。由于同一文件中的所有函数都能引用全局变量的值,当需要从一个函数中带回多个值时,就能克服函数调用只能返回一个值的局限性。不成文约定:全局变量名首字母大写。见P185例8.152.如无必要,不要使用全局变量。因为全局变量既降低程序的清晰
性和函数的通用性,且又在程序的全部执行过程中都占用存储空间。3.在文件开头定义的外部变量才可在整个文件范围内使用,若在定义点之前的函数需引用外部变量,则可用关键字“extern”作“外部变量说明”。注意:外部变
量定义和外部变量说明并不是同一回事。外部变量的定义只能有一次,它的位置在所有函数之外。而同一程序中的外部变量说明可以有多次,它的位置在函数之内(哪个函数要用就在哪个函数中说明)。系统根据外部变量的定义(而不是根据外部变量的说明)分配存储单元。
对外部变量的初始化只能在“定义”时进行“extern”只是申明该变量是一个已在外部定义过的变量而已。4.如果在同一源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。“屏蔽作用”。见P187例8.16•外部变量的定义与说明。•intvs(intxl,in
txw)•{externintxh;/*外部变量xh的说明*/•intv;•v=xl*xw*xh;/*直接使用外部变量xh的值*/•returnv;•}•main()•{externintxw,xh;/*外部变量的说明*/•intxl=5;/*内部变量的定义*/•printf(
"xl=%d,xw=%d,xh=%d\nv=%d",xl,xw,xh,vs(xl,xw));•}•intxl=3,xw=4,xh=5;/*外部变量xl、xw、xh的定义*/•程序区静态存储区动态存储区数据,变量存放内存
分配8.9变量的存储类别(生存期、生命期)•变量的存储方式•变量从空间上分为局部变量、全局变量。•从变量存在的时间的长短(即变量生存期)来划分,变量还可以分为:动态存储变量、静态存储变量。变量的存储方式决定
了变量的生存期。如果全局变量用static修饰,并不是说是静态的,而是说,只对本模块有效。自动(局部变量)(auto)动态存储方式寄存器(局部变量)(register)存储方式静态(局部变量)(static)静态存储
方式静态全局变量(全局变量全部是静态的,不必用static修饰)静态存储变量:存放于静态存储区,在程序整个运行过程中,始终占据固定的内存单元。动态存储变量:存放于动态存储区,根据程序的运行状态(如:函数调用)而临时分配的单元,且单元并不固定。每个变量或函数都有两个属性:数
据类型和数据存储类别•8.9.2auto变量•auto型存储方式是C语言默认的局部变量的存储方式,也是局部变量最常使用的存储方式。•自动变量属于局部变量的范畴,作用域限于定义它的函数或复合语句内。•自动变量所在的函数或复合语句执行时,系统动态为相应的自动变量分配存储单
元,当自动变量所在的函数或复合语句执行结束后,自动变量失效,它所在的存储单元被系统释放,所以原来的自动变量的值不能保留下来。若对同一函数再次调用时,系统会对相应的自动变量重新分配存储单元。•定义格式:[auto]类型说明变量名;•auto为自动存储类别关键词,可以
省略,缺省时系统默认auto.•8.9.3用static声明局部变量•静态局部变量的定义格式:•static类型说明变量名[=初始化值];•static是静态存储方式关键词,不能省略。•静态局部变量的存储空间是在程序编译时由系统分配的,且在程序运行的整个期间都固定不变。该类变量在其
函数调用结束后仍然可以保留变量值。下次调用该函数,静态局部变量中仍保留上次调用结束时的值。•静态局部变量的初值是在程序编译时一次性赋予的,在程序运行期间不再赋初值,以后若改变了值,保留最后一次改变后的值,直到程序运行
结束。例:求n!intfac(n)intn;{staticintf=1;f=fn;return(f);}main(){inti;for(i=1;i<=5;i++)printf("%d!=%d\n",i,fac(i));}运行结果为:1!=12!=23!=64!=245!=120每一次调
用fac(i),打印一个i!,同时保留这个i!的值以便下次再乘(i+1)。•8.9.4register变量•register变量一般分配register给相应变量。寄存器比内存操作要快很多,所以可以将一些需要反复操作的局部变量存放在寄存器中。•定义格式:regis
ter类型说明变量名•8.9.5用extern声明全局变量•全局变量作用域:从定义全局变量的位置起到本源程序结束为止。•在引用全局变量时如果使用“extern”声明全局变量,可以扩大全局变量的作用域。•在文件内声明,全局变量从声明处开始作用。•在别的文件声明,作用域扩展到别的文
件。•8.9.6用static声明全局变量•1.全局变量全部是静态存储的•因为全局变量全部是静态存储,所以没有必要为说明全局变量是静态存储而使用关键词static。•2.全局变量的static定义•全局变量的static定义,不是说明“此全
局变量要用静态方式存储”(全局变量天生全部是静态存储),而是说,这个全局变量只在本源程序模块有效(文件作用域)。•一个C语言程序可能是由多个源文件构成、在某个源文件中定义的全局变量可以使用“extern”声明,扩大到其它源文件。也可以使用static声明只在本源程序模块使用。
•变量的声明和定义•广义上讲,声明包括“定义性声明”(建立存储空间,即指名数据类型),简称定义。还包括“引用型声明”如externa,不建立存储空间。狭义上的声明为后者8.10内部函数和外部函数函数本身在一个文件中为全局的。即一个
文件中定义的函数可被该文件的所有其它函数引用。根据函数能否被其他源文件调用,函数分为内部函数与外部函数。•8.10.1内部函数•类似于全局变量的作用域,函数定义时也可以用static修饰。使用stat
ic修饰的函数是内部函数。•static函数类型函数名(形参表){……}•内部函数:只能被本源文件(模块)中的各个函数所调用,不能为其它模块中函数所调用的函数。•“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。•使用内部函数的好处是:不同的人编
写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。•8.10.2外部函数•外部函数:能被任何源文件(模块)中的任何函数所调用的函数•extern函数类型函数名(形参表)•说明:•外部函数定义,extern关键词可以省略。如果省略,默认是外部函数。•外部
函数可以在其它模块中被调用。如果需要在某个模块中调用它,可以模块中某个位置声明extern函数类型函数名(形参表);就可以了。例:有一个字符串,内有若干个字符,程序将字符串中该字符删除去。用外部函数实现。file1.c
(文件1)main(){externenter_string(),delete_string(),print_string();/*说明本文件要用到其它文件中的函数*/charc;staticcharstr[80];ente
r_string(str);scanf("%c",&c);delete_string(str,c);print_string(str);}file2.c(文件2)#include"stdio.h"externenter_st
ring(str)/*定义外部函数enter_string*/charstr[80];{gets(str);}/*读入字符串str*/file3.c(文件3)externdelete_string(str,ch)/*定义外部函数delete_string*/cha
rstr[],ch;{inti,j;for(i=j=0;str[i]!='\0';i++)if(str[i]!=ch)str[j++]=str[i];str[i]='\0';}file4.c(文件4)externprint
_string(str)/*定义外部函数print_string*/charstr[];{printf("%s",str);}运行情况如下:abcdefgc(输入str)c(输入要删去的字符)ab
defg(输出已删去指定字符的字符串)编译:分别编译file1.obj,file2.obj,file3.obj,file4.obj连接linkfile1+file2+file3+file4主函数或者在main()中#include"file2.c"#includ
e"file3.c"#include"file4.c"编译时,将上述三个文件中的函数插在file1.c前面。