C语言程序设计课件 (8)

上传人:dja****22 文档编号:242970373 上传时间:2024-09-13 格式:PPT 页数:88 大小:1.16MB
收藏 版权申诉 举报 下载
C语言程序设计课件 (8)_第1页
第1页 / 共88页
C语言程序设计课件 (8)_第2页
第2页 / 共88页
C语言程序设计课件 (8)_第3页
第3页 / 共88页
资源描述:

《C语言程序设计课件 (8)》由会员分享,可在线阅读,更多相关《C语言程序设计课件 (8)(88页珍藏版)》请在装配图网上搜索。

1、单击此处编辑母版标题样式,,单击此处编辑母版文本样式,,第二级,,第三级,,第四级,,第五级,,,,*,7.1 函数,,7.2 函数的定义,,7.3 函数的参数和返回值,,7.4 函数的调用,,7.5 函数的嵌套与递归调用,,7.6 数组与函数参数,,7.7 指针与函数,,7.8 变量的存储类别,,7.9 模块化程序设计方法,,补充:如何运行一个多文件的程序,第七章 函数与模块化程序设计方法,,1,,一个较大的程序一般应分为若干个程序模块,每一个模块,,用来实现一个特定的功能.一个C程序可由一个主函数和若干,,个函数构成。由主函数调用其他函数,其他函数也可以互相调,,用。同一个函数可以被一个

2、或多个函数调用任意多次。,,下图是一个程序中函数调用的示意图:,mian,g,e,d,e,f,g,h,h,i,c,b,a,函数概述,,,2,,main(),/* 主函数 */,,{,,print_star();,/*调用print_star函数画****/,,,print_message();,/*调用print _message函数写字*/,,,print_star();,/*调用print_star函数画****/,,},,print_star(),/*定义print_star函数*/,,{,,prinf(“\n**********”);,,},,print_message(

3、),/*定义print_message函数*/,,,{,,prinf(“\n Hello! ”);,,},函数调用例:,运行结果:,**********,,Hello!,,**********,,,3,,说明:,,(1)一个源程序文件由一个或多个函数组成。一个源程序文件,,是一个编译单位,即以源程序为单位进行编译,而不是以,,函数为单位进行编译。,(2)一个,C,程序由一个或多个源程序文件组成。一个源文件可,,以为多个,C,程序公用。,(3),C,程序的执行从,main,函数开始,调用其他函数后流程回到,,,main,函数,在,main,函数中结束整个程序的运行。,main,函数,,是系统

4、定义的。,(4)所有函数都是平行的,即在定义函数时是互相独立的,一,,个函数并不从属于另一函数,函数间可以互相调用,但不,,能调用,main,函数。,,4,,(5)从用户使用的角度看,函数有两种:,,①,标准函数,,即库函数。这是由系统提供的,用户不必自己,,定义这些函数,可以直接使用它们。,,② 用户自己定义的函数。用以解决用户的专门需要。,(6)从函数的形式看,函数分两类:,,①,无参函数,。,②,有参函数,。在调用函数时,在主调函数和被调用函数之间,,有数据传递。也就是说,主调函数可以将数据传给被调用,,函数使用,被调用函数中的数据也可以带回来供主调函数,,使用。,,5,,1、无参函数

5、的定义形式,,类型标识符 函数名(),,{ 声明部分,,语句,,},2、有参,函数定义的一般形式,,类型标识符 函数名(形式参数表列),,{ 声明部分,,语句,,},,函数的定义,,6,,例如:,,,int max(int x,int y),,{ int z;,,z=x>y? x:y;,,return(z);,,},,7,,3、可以有“空函数”,它的形式为:,,,类型说明符 函数名(),,{ },例如:,,dumy() {},,8,,4、对形参的声明的传统方式,,在老版本,C,语言中,对形参类型的声明是放在函数定义的,,第2行,也就是不在第l行的括号内指定形参的类型,而在括号,,外单

6、独指定,例如:,int max(x,y),,int x,y;,,{ int z;,,z=x>y?x:y;,,return(z);,,},一般把这种方法称为传统的对形参的声明方式,而把前面,,介绍过的方法称为现代的方式。,Turbo C,和目前使用的多数,C,版,,本对这两种方法都允许使用,两种用法等价。,,9,,1、形式参数和实际参数,,在定义函数时函数名后面括弧中的变量名称为“,形,,式参数,”,在主调函数中调用一个函数时,函数名后面,,括弧中的参数(可以是一个表达式)称为“,实际参数,”。,函数的参数和返回值,,,10,,例:调用函数时的数据传递,,main(),,{,,int a,b,c

7、;,,scanf(“%d,%d”,,,c=max(a,b);,,printf(“Max is %d”,c);,,},,max(int x,int y),,{,,int z;,,z=x>y?x:y;,,return(z);,,},运行情况:,,7,8,,Max is 8,,11,,(1)在定义函数中指定的形参,在未出现函数调用时,它们并,,不占内存中的存储单元。只有在发生函数调用时,函数,,,max,中的形参才被分配内存单元。在调用结束后,形参所,,占的内存单元也被释放。,(2)实参可以是常量、变量或表达式,如:,,,max(3,a+b);,,但要求它们有确定的值。在调用时将实参的值赋给形参,,

8、(如果形参是数组名,则传递的是数组首地址而不是数组,,的值)。,关于形参与实参的说明:,,12,,(3)在被定义的函数中,必须指定形参的类型。,,(4)实参与形参的类型应相同或赋值兼容。,,(5),C,语言规定,实参变量对形参变量的数据传递是“值传递”,,,即单向传递,只由实参传给形参,而不能由形参传回来给,,实参,在调用函数时,给形参分配存储单元,并将实参对,,应的值传递给形参,调用结束后,形参单元被释放,实参,,单元仍保留并维持原值。,,13,,函数的返回值,(1)函数的返回值是通过函数中的,return,语句获得的。,return,,,语句将被调用函数中的一个确定值带回主调函数中去。,,

9、,return,,语句后面的括弧也可以不要,如:,return z;,,,return,后面的值可以是一个表达式。,,,max(int x,int y),,,,{,,return(x>y?x:y);,,},,14,,,(2)函数值的类型。,,在定义函数时指定函数值的类型。例如:,,int max(float x,float y),/,★,函数值为整型,★,/,,char letter(char c1,char c2),/,★,函数值为字符型,★,/,,double min(int x,int y),/,★,函数值为双精度型,★,/,,15,,,C,语言规定,凡不加类型说明的函数,一律自动按整型

10、处,,理。在定义函数时对函数值说明的类型一般应该和,return,语句,,中的表达式类型一致。,例 返回值类型与函数类型不同。,main(),,{,,float a,b;,,int c;,,scanf(“%f,%f,”,,,c=max(a,b);,,printf(“”Max is%d\n”,c);,,} max(float x,float y);,,{ float z;,/,★,z为实型变量,★,/,,z=x>y?x:y;,,return(z);,,},运行情况如下:,,1.5,2.5,,Max is 2,,16,,(3)如果函数值的类型和,return,语句中表达式的值不一致,则,,

11、以函数类型为准。对数值型数据,可以自动进行类型转,,换。即函数类型决定返回值的类型。,(4)如果被调用函数中没有,return,语句,并不带回一个确定的、,,用户所希望得到的函数值,但实际上,函数并不是不带回,,值,而只是不带回有用的值。带回的是一个不确定的值。,(5)为了明确表示“不带回值”,可以用“void”定义“无类型”,,(或称“空类型”),,17,,1,、函数调用的一般形式,,函数名(实参表列);,如果是调用无参函数,则“实参表列”可以没有,但括弧不,,能省略,如果实参表列包含多个实参,则各参数间用逗号隔开。,,实参与形参的个数应相等,类型应一致。实参与形参按顺序对,,应,一一传递数

12、据。如果实参表列包括多个实参,对实参求值,,的顺序并不是确定的,有的系统按自左至右顺序求实参的值,,,有的系统则按自右至左顺序。,,许多,C,版本(例如,Turbo,,C,和,MS C,)是按自右而左的顺序,,求值。,,函数的调用,,,18,,2,、函数调用的方式,,按函数在程序中出现的位置来分,可以有以下三种函数,,调用方式:,(1)函数语句,,把函数调用作为一个语句。如:,printstar();,,19,,(2)函数表达式,,函数出现在一个表达式中,这种表达式称为函数表达式。,,这时要求函数带回一个确定的值以参加表达式的运算。例如:,,,c=2*max(a,b);,(3)函数参数,,函数

13、调用作为一个函数的实参。例如:,,m=max(a,max(b,c));,,20,,3,、对被调用函数的声明和函数原型,,在一个函数中调用另一函数(即被调用函数)需要具备的条件,(1)首先被调用的函数必须是已经存在的函数(是库函数或用,,户自己定义的函数)。,(2)如果使用库函数,一般还应该在本文件开头用,#include,命,,令将调用有关库函数时所需用到的信息“包含”到本文件中,,来。例如,前几章中已经用过的,,,#include ,(3)如果使用用户自己定义的函数,而且该函数与调用它的函,,数(即主调函数)在同一个文件中,一般还应该在主调函,,数中对被调用的函数作,声明,,即对编译系统声明

14、将要调用,,此函数,并将有关信息通知编译系统。,,21,,例 对被调用的函数作声明,main(),,{,,float add(float x,float y); /,★,对被调用函数的声明,★,,/,,float a,b,c;,,scanf(“%f,%f”,,,c=add(a,b);,,printf(“sum is%f”,c);,,},,float add(float x,float y); /,★,函数首部,★,,/,,{,,float z; /,★,函数体,★,,/,,z=x+y;,,return(z)

15、;,,},运行情况如下:,,3.6,6.5,,sum is 10.000000,,,22,,在函数声明中也可以不写形参名,而只写形参的类型。如:,,,float add,(,float,,,float,);,在,C,语言中,以上的函数声明称为,函数原型,(,function prototype),。,,它的作用主要是利用它在程序的编译阶段对调用函数的合,,法性进行全面检查。,函数原型的一般形式为,①,函数类型 函数名(参数类型1,参数类型2……),②,函数类型 函数名(参数类型1,参数名1,参数类型2,,,参数名2……),第①种形式是基本的形式。为了便于阅读程序,也允许在,,函数原型中加上参

16、数名,就成了第②种形式。但编译系统不检,,查参数名。因此参数名是什么都无所谓。,,23,,说明:,①,如果在函数调用之前,没有对函数作声明,则编译系统会把,,第一次遇到的该函数形式(函数定义或函数调用)作为函数的,,声明,并将函数类型默认为,int,型。,如果函数类型为整型,可以在函数调用前不必作函数声明。,,但是使用这种方法时,系统无法对参数的类型做检查。若调用,,函数时参数使用不当,在编译时也不会报错。因此,为了程序,,清晰和安全,建议都加以声明为好。,,②,如果被调用函数的定义出现在主调函数之前,可以不必加以,,声明。,,24,,③,如果已在所有函数定义之前,在函数的外部已做了函数声,,

17、明,则在各个主调函数中不必对所调用的函数再作声明。,,例如:,char letter(char,char);,/,★,以下3行在所有函数之前,且在函数外部,★,/,,float f(float,float);,,int i(float,float);,,main(),,{…} /,★,不必声明它所调用的函数,★,/,,char letter(char c1,char c2),/,★,定义,letter,函数,★,/,,{…},,float f(float x,float y),/,★,定义f函数,★,/,,{…},,int i(float

18、j,float k),/,★,定义i函数,★,/,,{…},,25,,例题:,写出下列程序的输出结果,(1),#include ,,s a,int b),,{ int temp;,,temp=a; a=b; b=temp;,,},,main(),,{ int x=7,y=11;,,printf("x=%d,\ty=%d\n",x,y);,,printf("s:\n");,,s);,,printf("x=%d,\ty=%d\n",x,y);,,},答案:,,x=7 y=11,,s:,,x=7 y=11,,,26,,(2),void s *p1,int *p2),,{ int

19、 p;,,p=*p1;,,*p1=*p2;,,*p2=p;,,},,main(),,{ int a,b;,,scanf("%d,%d",,,printf(“a=%d,b=%d\n”,a,b);,,printf(“s:\n”);,,s);,,printf(”a=%d,b=%d\n",a,b);,,},答案:,,x=7 y=11,,s:,,x=11 y=7,,,27,,分析: 通过对上面两程序,理解两种参数传递方式的区别:,,•值传递方式,,函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值。特点是形参与实参占用不同的内存单元,单

20、向传递。,,,,•地址传递,,函数调用时,将数据的存储地址作为参数传递给形参,特点是形参与实参占用同样的存储单元,“双向”传递,实参必须是地址常量或变量,形参是地址变量。,,,28,,,C,语言的函数定义都是互相平行、独立的,也就是说在,,定义函数时,一个函数内不能包含另一个函数。,,,C,语言不能嵌套定义函数,但可以嵌套调用函数,也就,,是说,在调用一个函数的过程中,又调用另一个函数。,main函数,a函数,b函数,调用a函数,调用b函数,结束,①,,②,,③,,④,,⑤,,⑥,,⑦,,⑧,,⑨,,函数的嵌套调用,,,29,,例 编一个程序计算下列函数值;,,要求为函数,p(i)、s(n)、

21、f(x,y),均编写一个用户函数,,,x、y,由主函数输入。,f(x,y)=,s(x),s(y),其中s(n)=∑ p(i)= p(1)+ p(2)+…+ p(n),p(i)=i!,n,i=1,,30,,float p(long i),,{ long k,j;,,for(k=j=1;j<=i;j++) k=k*j;,,return((float)(k));,,},,float s(long n),,{ float sum=0.0;,,long k;,,for(k=1;k<=n;k++),,sum=sum+p(k);,,return(sum);,,},float f(long x,long y

22、),,{ float f1;,,f1=s(x)/s(y);,,return(f1);,,},,main(),,{ long x1,y1;,,scanf(“%1d,%1d”,,,printf(“f(x,y)=%f\n”,f(x1,y1));,,},,31,,上述程序中,函数,f(),通过函数,s(),调用了函数,p(),,主函数通,,过函数,f(),调用函数,s(),都称为两层嵌套调用;主函数调用通过函,,数,f(),、,s(),调用函数,p(),称为三层嵌套调用。,,,原则上说,,,C,语言不限制嵌套调用的层数,,,嵌套调用层数仅,,受计算机内存的限制。,,,32,,,在调用一个函数的过程中又

23、出现直接或间接调用该函数,,本身,称为函数的递归调用。,,1,、递归调用的定义:,,2、例:,编一个计算,n!(n>1),的递归调用函数。,,float p(long n),,{ if(n<1L),,{ printf(“input error!\n”);,,return(-1);,,},,else if(n= =1L) return (1);,,else return(n*p(n-1));,,},,函数的递归调用,,,33,,如果有一条调用语句“,k=p(4);,”,,用图示的方式描述其执行过程。,返回,,p(4),,的值,,为,24,返回,p(3),,的值为,6,返回,p(2),,的值为,2

24、,返回,p(1),的值为,1,要计算,4*p(3),要计算,2*p(1),要计算,3*p(2),有返回值为,1,k=p(4);,对求阶乘的递归函数来说,这两个条件可以写成下列公式:,,递归计算公式,p(n)=n*p(n-1),,递归结束条件,,p(1)=1,,例:对求阶乘的递归函数来说,这两个条件可以写成下列公式,,P(n)=,n*p(n-1) n>1,1 n=0,1,,34,,我们把向下的递归调用过程称为“,递归过程,”,,,把向上的携带,,返回值计算返回表达式的过程称为“,回溯过程,”。其中递归过程,,的关键在于每次递归时,,,参数的值都要逐步趋向某个

25、特定值,(,例,,子中这个特定值是,1),。当递归到参数值等于特定值时,,,递归过程,,结束,,,此时一定要有返回值。,,4,、例,Hanoi,塔问题,,古代有一个梵塔,塔内有三个座,a、b、c,,开始时a座有三个,,盘子,盘子大小不等,大的在下,小的在上,要把这三个盘子,,从,a,座移到,c,座,但每次只允许移动一个盘,且在移动过程中在,,三个座上都始终保持大的在下,小的在上。,,35,,move(char x,char y),,{void,,printf(“%c,%c\n”,x,y);,,},,void hanoi(int n,char one,char two,char three),

26、,{,,if(n==1) move(one,three);,,else,,{,,hanoi(n-1,one,three,two);,,move(one,three);,,hanoi(n-1,two,one,three);,,},,},,36,,main(),,{,,int m;,,printf(“input the number of diskes:”);,,scanf(“%d”,&m),,printf(“The step to moving %3d,,diskes:\n”,m);,,hanoi(m,’A’,’B’,’C’);,,},运行结果:,,input the number of dis

27、kes:3,,The step to moving 3 diskes:,,A,C,,A,B,,C,B,,A,C,,B,A,,B,C,,A,C,,37,,1,、数组元素作函数实参,,例 有两个数组,a、b,,各有,10,个元素,将它们对应地逐个相,,比(即,a[0],与,b[0],比,,a[1],与,b[1],比……)。如果,a,数组中的元,,素大于,b,数组中的相应元素的数目多于,b,数组中元素大于,a,数组,,中相应元素的数目(例如,,a[i]>b[i],,6,次,,b[i]>a[i],,3,次,其中,i,,每次为不同的值),则认为,a,数组大于,b,数组,并分别统计出

28、两,,个数组相应元素大于、等于、小于的次数。,程序如下:,,数组作为函数参数,,,38,,main(),,{ int a[10], b[10],i,n=0,m=0,k=0;,,printf(“enter array a:\n”);,,for(i=0;i<10;i++),,scanf(“%d”,,,printf(“\n”);,,printf(“enter array b:\n”);,,for(i=0;i<10;i++),,scanf(“%d”,,,printf(“\n”);,,for(i=0;i<10;i++),,{ if(large(a[i],b[i])==1)n=n+1;,,else if

29、(large(a[i],b[i])==0)m=m+1;,,else k=k+1;},,39,,printf(“a[i]>b[i]%d times\na[i]=b[i]%d times\na[i]k) printf(“array a is larger than array b\n”);,,else if(n

30、array b\n”);,,},,large(int x,int y),,{int flag;,,if(x>y)flag=1;,,else if(xb[i] 4 times,,a[i]= b[i] 1 times,,a[i]< b[i] 5 times,,array a is smaller

31、 than array b,,,40,,float average(float array[10]),,{int i;,,float aver,sum=array[0];,,for(i=1;i<10;i++),,sum=sum+array[i];,,aver=sum/10;,,return(aver);,,},2,、数组名可作函数参数,,例 有一个一维数组score,内放10个学生成绩,求平均成绩。,,程序如下:,,,41,,main(),,{float score[10],aver;,,int i;,,printf(“intput 10 scores:\n”),,for(i=0;i<10;i

32、++),,scanf(“%f”,,,printf(“\n”);,,aver=average(score);,,printf(“average score is %5.2f”,aver);,,},运行结果:,,input 10 scores:,,100 ,56, 78, 98.5, 76, 87, 99, 67.5, 75 ,97,,average score is 83.40,,42,,说明:,,(1)用数组名作函数参数,应该在,主调函数,和被,调用函数,分别,,定义数组。,(2),实参数组,与,形参数组,类型应一致(今都为,float,型),如不,,一致,结果将出错。,(3),实参数组,和,

33、形参数组,大小可以一致也可以不一致,C编译,,对,形参数组,大小不做检查,只是将,实参数组,的首地址传给,,形参数组。,,43,,(4),形参数组,也可以不指定大小,在定义数组时在数组名后面,,跟一个空的方括弧,为了在被调用函数中处理数组元素的,,规模,可以另设一个参数,传递数组元素的个数。,(5)用数组名作函数实参时,不是把数组的值传递给形参,而,,是把实参数组的起始地址传递给,形参数组,,这样两个数组,,就共占同一段内存单元。,,44,,3,、用多维数组名作函数参数,,例 有一个3×4的矩阵,求所有元素中的最大值。,,程序如下:,,max_value(int array[ ][4]),,{

34、int i,j,k,max;,,max=array[0][0];,,for(i=o;i<3;i++),,for(j=0;i<4;j++),,if(array[i][j]>max) max=array[i][j];,,return(max);,,},,main(),,{ int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};,,printf(“max value is %d\n”,max_value(a));,,},运行结果如下:,,max value is 34,,45,,指向函数的指针,指针变量可以指向变量地址、数组、字符串、动态分配地址,,同时也可

35、指向函数,,每一个函数在编译时,系统会分配给该函数一个人口地址,,函数名表示这个人口地址,,那么,,指向函数的指针变量称之函数指针变量.,例如:,,float fun(int,char);,,float (* p)( );,,p=fun;,,,,46,,2.用函数指针变量调用函数,,,,,可用函数指针来调用函数.,,其形式为:,,(*函数指针变量名)(),,,,例如:,,int i=5;,,char ch=‘a’;,,float fun(int,char),(*p)( );,,p=fun;,,(*p)(i,ch);,,…,,int i=5,;,,char ch=’a’,;,,float fun

36、(int,,,char),;,,fun(i,,,ch),;,,,47,,[,例,7,.,18],利用直接函数调用,求函数返回值。,#include,,main(),,{,,int a,=,3,,,b=5,;,,float f1(),,,f2(),;,,printf(,”%,4,.,1f,\,n,”,,f1(a,,,b)),;,,printf(,”%,4,.,1f,\,n,”,,f2(a,,,b)),;,,},,float f1(int x,,,int y),,{ return x+y,;,},,float f2(int x,,,int y),,{ ret

37、urn(1,.,0*y),/,x,;,},运行结果:,,8.0,,1.7,,,48,,[,例,7,.,19],利用函数指针变量调用,求函数返回值,,,#include ,,sub(int x,,,int y,,,float(*fun)(),),,{ float result,;,,result,=,(*fun)(x,,,y),;,,printf(,”%,4.1f,\,n,”,,result),;,,,},,float f1(int x1,int y1),,{ return x1+y1; },,float f2(int x2,int y2),,{ return (y2*1.0)/x2; },

38、,main( ),,{ int a=3,,,b=5,;,,float fl( ),,,f2(),;,,,sub(a,,,b,,,f1),;,,sub(a,,,b,,,f2),;,,,},,,49,,返回指针的函数,1.定义,,函数返回值可以是int、char、float等,也可以为地址值。,,函数返回值是地址值的函数称指针函数.,,也就是,定义该函数的类型为指针类型。该函数就一定有相,,应指针类型的返回值。,返回值必须用同类型的指针变量来,,接受。,例如:,,,float *fun(),;,,float,,*p,;,,p,=,fun(a),;,,,,50,,[例] 指针函数的含义。,,ma

39、in(),,{,,float a[ ]={1,2,3,4,5},*p,*fun();,,p=fun(a); /*指针函数的调用形式*/,,printf(”%3.1f\n”,*p);,,},,float *fun(float *x),,{,,return ++x;,,},,51,,[,例,],两个字符串的连接。,,include ,,#define N1 20,,#define N2 10,,main( ),,{,,char *s,,,s1[Nl],,,s2[N2];,,char *strsl2(char *,,,char *),;,,

40、gets(s1),;,,gets(s2),;,,s,=,strsl2(sl,,,s2),;,,,printf(,”,s12,=%,s,\,n,”,,s),;,,},,char *strsl2(char *pl,,,char *p2),,{,,char *temp,;,,temp=pi,;,,,while(*p1),,p1++,;,,,while(*p2),,{ *pl,=,*p2,;,,pl++,;,,,p2++,;,,},,p1,=,’,\,0’,;,,return temp,;,,},,,运行结果:,,I am a good,,student,/,*,以上是键盘输入,*,/,

41、,I,,arn,,a,,good,,student,/,*,输出结果,*,/,,52,,变量的作用域:,局部变量与全局变量的应用,,1,、局部变量:,在一个函数内部定义的变量是内部变量,,,只在,,本函数内部有效。,2,、全局变量:,在函数外定义的变量称为外部变量,外部变量,,是全局变量,全局变量可以为本文件中其它函,,数共用,它的有效范围为从定义变量的位置开,,始到本源文件结束。,,,53,,例:,变量的作用域。,,#include,"stdio.h",,int,n=65;,,void,print(),,{,,printf("%d\n",n);,,},,void,main(),,{,,pri

42、ntf("%d\n",n);,,print();,,},,,,程序运行为,,65,,65,,,我们可以看到,定义了全局变量,无论是主函数还是自定义的函数,都可以使用全局变量。,,,54,,如果我们把程序改成这样:,,#include,"stdio.h",,void,print(),,{,,printf("%d\n",n);,,},,void,main(),,{,int,n=65;,,printf("%d\n",n);,,print();,,},则在编译时,系统会提示在print()函数内的变量n未定义。其实在这个程序中,主函数中定义的变量n是一个局部变量,它的范围只是在主函数内,不能用在主函数

43、之外的地方。,,,55,,例,有一个一维数组,内放10个学生成绩,写一个函数,求,,出平均分、最高分和最低分。,程序如下:,,,56,,float Max=0,Min=0;,,float average(float array[ ],int n),,{ int i;,,float aver,sum=array[0];,,Max=Min=array[0];,,for(i=1;iMax)Max=array[i];,,else if(array[i]

44、,return(aver);,,},,57,,main(),,{ float ave,score[10];,,int i;,,for(i=0;i<10;i++),,scanf(“%f”,,,ave=average(score,10);,,printf(“max=%6.2f\n,,min=%6.2f\n,,average=%6.2f\n”,,,Max,Min,ave);,,},运行结果如下:,,99 45 78 97 100 67.5 89 92 66 43,,max=100.00,,min=43.00,,average=77.65,,58,,注意:,,(1),设全局变量的作用是增加了函数间数据

45、联系的渠道。由,,于同一文件中的所有函数都能引用全局变量的值,因此,,如果在一个函数中改变了全局变量的值,就能影响到其,,它函数,相当于各个函数间有直接的传递通道。,,59,,(2),建议不在必要时不要使用全局变量,因为:,① 全局变量在程序的全部执行过程中都占用存储单元,而不是,,仅在需要时才开辟单元。,② 它使函数的通用性降低了,因为函数在执行时要依赖于其所,,在的外部变量。,③ 使用全局变量过多,会降低程序的清晰性,人们往往难以清,,楚地判断出每个瞬时各个外部变量的值。,(3),如果在同一个源文件中,外部变量与局部变量同名,则在,,局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。

46、,,60,,例,外部变量与局部变量同名。,int a=3,b=5;,/,★,a、b为外部变量,★,/,,max(int a,int b),/,★,a、b为局部变量,★,/,,,{ int c;,,c=a>b?a:b;,,return(c);,,},,main(),,{ int a=8;,/,★,a为局部变量,★,/,,,printf(“%d”,max(a,b));,,},形参a、b作用范围,,a、b作用范围,,局部变量a作用范围,,全局变量b的作用范围,,运行结果为:,,8,,61,,静态存储方式:指在程序运行期间分配固定的存储空间的方式。,,动态存储方式:指在程序运行期间根据需要进行动态的分

47、配存,,储空间的方式。,用户使用的存储空间可以分为三部分,如右图所示:,,① 程序区,,② 静态存储区,,③ 动态存储区,,程序区,静态存储区,动态存储区,用 户 区,,动态存储方式与静态存储,,62,,数据分别存放在静态存储区和动态存储区中。全局变量全,,部存放在静态存储区中,在程序开始执行时给全局变量分配存,,储区,程序执行完毕就释放。程序执行过程中它们占据固定的,,存储单元,而不是动态地进行分配和释放。,在动态存储区中存放以下数据:,,① 函数形式参数。,,② 自动变量(未加,static,声明的局部变量),,③ 函数调用时的现场保护和返回地址等。,,63,,局部静态变量,1.局部静态变

48、量,,(1)定义格式: static 数据类型 内部变量表;,,(2)存储特点,3)何时使用局部静态变量,,1)需要保留函数上一次调用结束时的值。,,2)变量只被引用而不改变其值。,1)静态内部变量属于静态存储。在程序执行过程中,即使所在函数调用结束也不释放。换句话说,在程序执行期间,静态内部变量始终存在,但其它函数是不能引用它们的。,2)定义但不初始化,则自动赋以"0"(整型和实型)或'\0'(字符型);且每次调用它们所在的函数时,不再重新赋初值,只是保留上次调用结束时的值!,,64,,例:,打印1到5的阶乘值。,int fac(int n),,{ static int f=1;,,f=

49、f*n;,,return(f);,,},,main(),,{ int i;,,for(i=1;i<=5;i++),,printf(“%d!=%d\n”,i,fac(i));,,},运行结果为:,,1!=1,,2!=2,,3!=6,,4!=24,,5!=120,,65,,(1),定义格式,:[,auto,] 数据类型 变量表;,,(2),存储特点,,1)自动变量属于动态存储方式。在函数中定义的自动变量,只在该函数内有效;函数被调用时分配存储空间,调用结束就释放。,,在复合语句中定义的自动变量,只在该复合语句中有效;退出复合语句后,也不能再使用,否则将引起错误。,,2)定义而不初始化,则其值是

50、不确定的。如果初始化,则赋初值操作是在调用时进行的,且每次调用都要重新赋一次初值。,,3)由于自动变量的作用域和生存期,都局限于定义它的个体内(函数或复合语句),因此不同的个体中允许使用同名的变量而不会混淆。即使在函数内定义的自动变量,也可与该函数内部的复合语句中定义的自动变量同名。,建议:,系统不会混淆,并不意味着人也不会混淆,所以尽量少用同名自动变量!,auto变量,,66,,例:自动变量与静态局部变量的存储特性。,,void auto_static(void),,{ int var_auto=0;,/*自动变量:每次调用都重新初始化*/,,static int var_static=

51、0;,/*静态局部变量:只初始化1次*/,,printf(“var_auto=%d, var_static=%d\n”, var_auto, var_static);,,++var_auto;,,++var_static;,,},,main(),,{ int i;,,for(i=0; i<5; i++) auto_static();,,},,67,,,一般情况下,变量的值都是存储在内存中的。为提高执行效率,C语言允许将局部变量的值存放到寄存器中,这种变量就称为寄存器变量。定义格式如下:,,,register,数据类型 变量表;,(1)只有局部变量才能定义成寄存器变量,即全局变量不行

52、。,,(2)对寄存器变量的实际处理,随系统而异。例如,微机上的MSC和TC 将寄存器变量实际当作自动变量处理。,,(3)允许使用的寄存器数目是有限的,不能定义任意多个寄存器变量。,寄存器存储──寄存器变量,,68,,外部变量的存储方式,外部变量属于静态存储方式:,,(1)静态外部变量──只允许被本源文件中的函数引用,,其定义格式为: static 数据类型 外部变量表;,(2)非静态外部变量──允许被其它源文件中的函数引用定义时缺省static关键字的外部变量,即为非静态外部变量。其它源文件中的函数,引用非静态外部变量时,需要在引用函数所在的源文件中进行说明:,,extern 数据类型

53、 外部变量表;,注意:在函数内的extern变量说明,表示引用本源文件中的外部变量!而函数外(通常在文件开头)的extern变量说明,表示引用其它文件中的外部变量,。,,,69,,静态局部变量和静态外部变量同属静态存储方式,但两者区别较大:,,(1)定义的位置不同。静态局部变量在函数内定义,静态外部变量在函数外定义。,(2)作用域不同。静态局部变量属于内部变量,其作用域仅限于定义它的函数内;虽然生存期为整个源程序,但其它函数是不能使用它的。,,静态外部变量在函数外定义,其作用域为定义它的源文件内;生存期为整个源程序,但其它源文件中的函数也是不能使用它的。,(3)初始化处理不同。静态局部变量,

54、仅在第1次调用它所在的函数时被初始化,当再次调用定义它的函数时,不再初始化,而是保留上1次调用结束时的值。而静态外部变量是在函数外定义的,不存在静态内部变量的“重复”初始化问题,其当前值由最近1次给它赋值的操作决定。,,70,,务必牢记,:,把局部变量改变为静态内部变量后,改变了它的存储方式,即改变了它的生存期。把外部变量改变为静态外部变量后,改变了它的作用域,限制了它的使用范围。因此,关键字“static”在不同的地方所起的作用是不同的。,,71,,模块化程序设计方法,,1、模块化程序设计方法的指导思想,,将一个大而复杂的设计任务按其需要实现的主要功能分解为若干相对独立的模块,将各模块的功能

55、逐步细化为一系列的处理步骤或某种程序设计语言的语句。,,完成总任务的程序由一个主程序和若干个子程序组成,主程序起着任务调度的总控作用,而每个子程序各自完成一个单一的任务。然后分别编写,调试,最后再将它们的目标模块连接装配成一个完整的整体。,模块化程序设计的优点是:程序编制方便,易于修改和调试,可由多人分工合作完成,程序的可读性。可维护性及可扩充性强;子程序的代码公用,使程序简洁。,,72,,2、模块分解的原则,(1)如果一个程序段被很多模块所公用,则它应是一个独立的模块。,,(2)如果若干个程序段处理的数据是公用的,则这些程序段应放在一个模块中。,,(3)若两个程序段的利用率差别很大,则应分属

56、于两个模块。,,(4)一个模块既不能过大,也不能过小。过大则模块的通用性较差,过小则会造成时间和空间上的浪费。,,(5)力求使模块具有通用性,通用性越强的模块利用性越高。,,(6)各模块间应在功能上,逻辑上相互独立,尽量截然分开,特别应避免用转移语句在模块间转来转去。,,(7)各模块间的接口应该简单,要尽量减少公共变量的个数,尽量不用共用数据存储单元,在结构或编排上有联系的数据应放在一个模块中,以免相互影响,造成查错困难。,,(8)每个模块的结构应设计成单入口,单出口的形式。这样的程序便于调试,阅读和理解且可靠性高。,,73,,1,、内部函数,,如果一个函数只能被本文件中其他函数所调用,它称为

57、,,内部函数。在定义内部函数时,在函数名和函数类型的前面,,加,static,。即,,static,,类型标识符 函数名(形参表),内部函数又称静态函数。通常把只能由同一文件使用的,,函数和外部变量放在一个文件中,在它们前面都冠以,static,使,,之局部化,其他文件不能引用。,内部函数和外部函数,,,74,,2,、外部函数,,(1)在定义函数时,如果在函数首部的最左端冠以关键字,,,extern,,则表示此函数是外部函数,可供其他文件调用。,如函数首部可以写为,,,extern int fun(int a,int b),C,语言规定,如果在定义函数时省略,extern,,则隐含为外部

58、函数。,(2)在需要调用此函数的文件中,用,extern,声明所用的函数是,,外部函数。,,75,,例,有一个字符串,内有若干个字符,今输入一个字符,要求,,程序将字符串中该字符删去。用外部函数实现。,(文件1),,main(),,{extern enter_string(char str[80]);,,extern delete_string(char str[ ],char ch);,,extern print_string(char str[ ]);,,char c;,,char str[80];,,enter_string(str);,,scanf(“%c”,,,delete_stri

59、ng(str,c);,,print_sting(str);,,},补充:如何运行一个多文件的程序,,,76,,file.c(文件2),,#include,,enter_string(char str[80]),,{ gets(str);},file.c(文件3),,delete_string(char str[],char ch),,{ int i,j;,,for(i=j=0;str[i]!=‘\0’;i++),,if(str[i]!=ch),,str[j++]=str[i];,,str[j]=‘\0’;,,},(文件4),,Print_string(char str[]),,{,,print

60、f(“%s”,str);,,},运行情况如下:,,abcdefgc,(输入str),,c,(输入要删去的字符),,abdefg,(输出已删去指定字符,,,的字符串),,77,,运行步骤:,(1)先后输入并编辑4个文件,并分别以文件名,、,,,、,存储在磁盘上。,(2)在编辑状态下,建立一个“项目文件”,它不包括任何程序,,语句,而只包括组成程序的所有的文件名。即,,,,,,如果这些源文件不在当前目录下,应指出路径。,,(3)将以上内容存盘,文件名自定,但扩展名必须为,.prj,。今,,设文件名为,a.prj,。,,78,,(4)按功能健,F9,,进行编译连接,系统先后将4个文件翻译成,,目标文

61、件,并把它们连接成一个可执行文件,a.exe,。,(5)按,Ctrl+F9,键,即可运行可执行文件,a.exe,。,,79,,课堂练习,,1、写一函数,统计字符串中字母的个数。要求用数组元素作为函数实参。,,提示:数组元素就是下标变量,它与普通变量并无区别。数组元素只能用作函数实参,其用法与普通变量完全相同:在发生函数调用时,把数组元素的值传送给形参,实现单向值传送。,,,80,,int isalp(char c),,{,,if (c>='a'&&c='A'&&c<='Z'),,return(1);,,else return(0);,,},,main(),,{ int i,num=0;,

62、,char str[255];,,printf("Input a string: ");,,gets(str);,,for(i=0;str[i]!='\0';i++),,if (isalp(str[i])) num++;,,puts(str);,,printf("num=%d\n",num);,,getch();,,},,,,,,81,,分析:,,(1)用数组元素作实参时,只要数组类型和函数的形参类型一致即可,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。,,(2)在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元

63、。在函数调用时发生的值传送,是把实参变量的值赋予形参变量。,,(3)用数组名作函数参数,应该在调用函数和被调用函数中分别定义数组,且数据类型必须一致,否则结果将出错。例如,在本案例中,形参数组为a[],实参数组为sco[],它们的数据类型相同。,,,82,,2、分别用直接递归调用和间接递归调用计算整数和:1+2+3+4+…+n。,答案:,,(1)直接递归,,long sum(int n),,{,,if(n==1) return(1);,,return(sum(n-1)+n);,,},,,,83,,(2)间接递归,,long acc(int n),,{ l

64、ong add(int);/*函数说明*/,,if(n<=1) return(1);,,return(add(n));,,},,long add(int n),,{,,long acc(int);/*函数说明*/,,return(acc(n-1)+n);,,},,,84,,分析:,,函数的递归调用是指一个函数直接或间接地调用了该函数本身,它有两种形式:,,直接递归调用,和间接递归调用。,,上例中将,sum(),函数改写成两个函数,acc( ),和,add( ),通过间接递归实现了计算。,,,,85,,3、输入长方体的长(l)、宽(w)、高(h),求长方体体积及正、侧、顶三个面的面

65、积。,提示:利用全局变量计算长方体的体积及三个面的面积,答案:,,86,,int s1,s2,s3;,,int vs(int a,int b,int c),,{ int v;,,v=a*b*c; s1=a*b; s2=b*c; s3=a*c;,,return v;,,},,main(),,{int v,l,w,h;,,clrscr();,,printf("\ninput length,width and height: ");,,scanf("%d%d%d",,,v=vs(l,w,h);,,printf("v=%d,s1=%d,s2=%d s3=%d\n",v,s1,s2,s3);,,getch();,,},,87,,分析:要注意局部变量和全局变量的区别:,,局部变量是内部变量,在函数内定义,只在本函数内有效。,,另外对于局部变量要注意:,,main,中定义的变量只在,main,中有效,,不同函数中同名变量,占不同内存单元,,形参属于局部变量,,可定义在复合语句中有效的变量,,局部变量可用存储类型:,auto,,,register,,,static,,(,默认为,auto,),,全局变量是外部变量,在函数外定义,可为本文件所有函数共用,有效范围为从定义变量的位置开始到本源文件结束,及有,extern,说明的其它源文件。,,88,,

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

相关资源

更多
正为您匹配相似的精品文档
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!