二级语言函数_第1页
二级语言函数_第2页
二级语言函数_第3页
二级语言函数_第4页
二级语言函数_第5页
已阅读5页,还剩78页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

二级语言函数第一页,共八十三页,编辑于2023年,星期五2023/5/231学习目标库函数的正确调用函数的定义方法函数的类型和返回值形式参数与实际参数,参数值的传递函数的正确调用,嵌套调用,递归调用局部变量和全局变量变量的存储类别(自动,静态,寄存器,外部),变量的作用域和生存期内部函数与外部函数第二页,共八十三页,编辑于2023年,星期五2023/5/232在C语言程序设计中,具有特定功能的子模块对应为“函数”;函数是一个命名的程序段,负责完成特定的、相对独立的动作或计算第三页,共八十三页,编辑于2023年,星期五2023/5/2337-1概述【例7-1】程序示例。

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);}运行时输入9,4结果为:

maxis9第四页,共八十三页,编辑于2023年,星期五2023/5/2347-1概述(1)一个源程序文件由一个或多个函数组成。一个源程序文件是一个编译单位,即以源文件为单位进行编译,而不是以函数为单位进行编译。第五页,共八十三页,编辑于2023年,星期五2023/5/2357-1概述(2)一个C程序由一个或多个源程序文件组成。对于较大的程序,一般不希望全放在一个文件中,而将函数和其它内容(如预编译命令)分别放到若干个源文件中,再由若干源文件组成一个C程序。这样可以分别编写、分别编译,提高调试效率。一个源文件可以为多个C程序公用。第六页,共八十三页,编辑于2023年,星期五2023/5/2367-1概述(3)C程序的执行从main函数开始,调用其它函数后流程回到main函数,在main函数中结束整个程序的运行。main函数是系统定义的。第七页,共八十三页,编辑于2023年,星期五2023/5/2377-1概述(4)所有函数都是平行的,即在定义函数时是互相独立的,一个函数并不从属于另一函数,即函数不能嵌套定义,但可以互相调用,甚至嵌套调用、递归调用(注:不能调用main函数)。第八页,共八十三页,编辑于2023年,星期五2023/5/2387-1概述(5)从用户使用的角度看,函数有两种:①标准函数,即库函数。这是由系统提供的,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。②用户自己定义的函数,以解决用户的专门需要。第九页,共八十三页,编辑于2023年,星期五2023/5/2397-1概述(6)从函数的形式看,函数分两类:①无参函数。在调用无参函数时,主调函数并不将数据传送给被调用函数,一般用来执行指定的一组操作。无参函数可以带回也可以不带回函数值,但一般以不带回函数值的居多。②有参函数。在调用函数时,在主调函数和被调用函数之间有参数传递,也就是说,主调函数可以将数据传给被调用函数使用,被调用函数中的数据也可以带回来供主调函数使用。第十页,共八十三页,编辑于2023年,星期五2023/5/23107-2函数定义的一般形式

7-2-1无参函数的定义无参函数定义形式:类型标识符函数名(){声明部分语句部分}例如:print(){printf("welcome");}printstar(){printf("*******");}main(){printstar();print();printstar();}第十一页,共八十三页,编辑于2023年,星期五2023/5/23117-2函数定义的一般形式

7-2-2有参函数的定义有参函数定义形式:类型标识符函数名(类型名形式参数1,类型名形式参数2…){声明部分语句部分}例如:

intmax(intx,inty)/*函数的首部*/{intz;/*函数体中的声明部分*/

z=x>y?x:y;/*语句部分*/

return(z);/*语句部分*/}第十二页,共八十三页,编辑于2023年,星期五2023/5/23127-2函数定义的一般形式

7-2-3空函数的定义定义形式:类型标识符函数名(){}例如:fun(){}调用此函数时,什么也不做。在主调函数中写上fun();表明这里要调用一个函数,而现在这个函数还没有完成,等以后扩充函数功能时补充上。第十三页,共八十三页,编辑于2023年,星期五2023/5/23137-3函数参数和函数的值

7-3-1形式参数和实际参数在调用函数时,大多数情况下,主调函数和被调用函数之间有数据传递关系。在定义函数时函数名后面括弧中的变量名称为“形式参数”(简称“形参”),在调用函数时,函数名后面括弧中的表达式称为“实际参数”(简称“实参”)。

第十四页,共八十三页,编辑于2023年,星期五2023/5/23147-3函数参数和函数的值

(1)在定义函数中指定的形参变量,在未出现函数调用时,它们并不占内存中的存储单元。只有在函数被调用时函数的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。第十五页,共八十三页,编辑于2023年,星期五2023/5/23157-3函数参数和函数的值

(2)形参只能是变量,而实参可以是常量、变量或表达式,在调用时将实参的值传递给形参(如果实参是数组名,则传递的是数组地址而不是变量的值,参见第8章)。(3)在被定义的函数中,必须指定形参的类型。第十六页,共八十三页,编辑于2023年,星期五2023/5/23167-3函数参数和函数的值

(4)实参与形参的个数应一样、类型应一致。如果实参为整型而形参为实型,或者相反,则会发生“类型不匹配”的错误。但编译程序一般不会给出错误信息,即使有时得不到确定结果,通常会继续运行下去。字符型与整型可以互相通用。第十七页,共八十三页,编辑于2023年,星期五2023/5/23177-3函数参数和函数的值

(5)C语言规定,实参对形参的数据传递是单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。第十八页,共八十三页,编辑于2023年,星期五2023/5/2318函数调用过程1.函数定义(编译)时,形参不占用内存空间,所以形参只是一个“符号”,没有值2.当函数被调用时,为形参分配内存空间,并把实参的值传递给形参,从此,实参与形参没有任何联系。3.当函数调用结束后,形参释放内存空间,形参消失。4.形参只能是动态局部变量,实参只要是合法的表达式就行。第十九页,共八十三页,编辑于2023年,星期五2023/5/23197-3函数参数和函数的值

【例7-2】通过以下程序能否计算a与b中值的和,请分析。main(){inta=1,b=2,c=0;/*(1)开辟三个存储单元a,b,c并分别存放1,2,0*/sum(a,b,c);/*(2)调用函数。a,b,c是实参,将它们的值1,2,0分别传给形参*/printf("c=%d\n",c);}/*(6)输出c中的值,因为是单向传递,z的改变不影响c的值*/sum(intx,inty,intz)/*(3)为形参x,y,z开辟单元,x,y,z分别从实参得到1,2,0*/{z=x+y;/*(4)执行函数体*/}/*(5)结束调用,x,y,z被释放*/运行结果是:c=0第二十页,共八十三页,编辑于2023年,星期五2023/5/23207-3函数参数和函数的值7-3-2函数的返回值通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。说明:(1)函数的返回值是通过函数中的return语句获得的。return语句的一般形式是:

return(表达式);或return表达式;或return;return语句中的表达式的值就是所求的函数值。如果return语句后面没有表达式,则没有确定的函数值。一个函数中可以有一个以上的return语句,但执行到其中一个return语句,就结束该函数的调用。第二十一页,共八十三页,编辑于2023年,星期五2023/5/23217-3函数参数和函数的值(2)如果不需要从被调用函数带回函数值,可以不要return语句,这时执行到函数体中的最后一条语句后,自动退出调用函数,而且带回不确定的返回值。(3)函数值的类型。既然函数可以有返回值,这个值当然应属于某一个确定的类型,应当在定义函数时指定函数值的类型。第二十二页,共八十三页,编辑于2023年,星期五2023/5/23227-3函数参数和函数的值(4)在定义函数时,对函数值声明的类型一般应该和return语句中的表达式类型一致。如果它们的类型不一致,则以函数值的类型为准。对数值型数据,可以自动进行类型转换,即函数类型决定返回值的类型。(5)为了明确表示“不带回值”,可以用“void”定义“无类型”(或称“空类型”)。例如:voidprintstar(){…}第二十三页,共八十三页,编辑于2023年,星期五2023/5/23237-3函数参数和函数的值

【例7-3】将例7-2改写成如下形式后,重新分析之。

#include<stdio.h>main(){inta=1,b=2,c;/*(1)开辟三个存储单元a,b,c,并分别给a,b赋1,2*/

c=sum(a,b);/*(2)调用函数.a,b是实参,将函数返回值赋给c*/

printf("c=%d\n",c);}/*(6)输出c的值,因为通过函数返回值给c,c的值改变*/

intsum(intx,inty)

/*(3)形参x,y分别从实参得到1,2,函数值的类型为整型*/

{intz;/*(4)在函数内使用的变量*/

z=x+y;returnz;/*(5)结束调用,x,y,z被释放,将z的值作为函数返回值*/

}运行结果是:c=3

第二十四页,共八十三页,编辑于2023年,星期五2023/5/23247-4函数的调用

7-4-1函数调用的形式函数调用的一般形式:

函数名(实参表列);如果是调用无参函数,则实参表列可以没有,但括弧不能省略。如果实参表列包含多个参数,则各参数间用逗号隔开。实参与形参的个数应相等,类型应一致。实参与形参按顺序对应,一一传递数据。第二十五页,共八十三页,编辑于2023年,星期五2023/5/23257-4函数的调用

【例7-4】写出运行结果main(){intk=2,t;t=f(k,++k);printf("%d",t);}intf(inta,intb){intc;if(a>b)c=1;if(a<b)c=-1;if(a==b)c=0;returnc;}在TurboC系统上运行结果为:0第二十六页,共八十三页,编辑于2023年,星期五2023/5/23267-4函数的调用

7-4-2函数调用的方式1.函数语句把函数调用作为一个语句。如:printstar();不要求函数带回值,只要求函数完成一定的操作。2.函数表达式函数出现在一个表达式中,这种表达式称为函数表达式,这时要求函数带回一个确定以参加表达式的运算。例如:c=2*max(a,b);3.函数参数把函数调用得到的值作为一个函数的实参例如:m=max(a,max(b,c));第二十七页,共八十三页,编辑于2023年,星期五2023/5/23277-4函数的调用

7-4-3对被调用函数的声明和函数原型(1)首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。(2)如果使用库函数,一般还应该在本文件开头用#include命令将调用有关库函数时所需用到的信息包含到本文件中来。例如#include"stdio.h"或#include<stdio.h>

第二十八页,共八十三页,编辑于2023年,星期五2023/5/23287-4函数的调用

7-4-3对被调用函数的声明和函数原型

(3)如果使用用户自己定义的函数,而且该函数与调用它的函数(即主调函数)在同一个文件中,一般还应该在文件的开头或在主调函数中对被调函数的类型进行声明,这种类型声明的一般形式为:返回值的类型名函数名();第二十九页,共八十三页,编辑于2023年,星期五2023/5/23297-4函数的调用

【例7-5】计算a与b的值的平方根之和。#include<stdio.h>#include<math.h>/*在函数中使用数学函数sqrt,因此要写此行*/main(){floatadd(float,float);

/*对被调函数的声明*/

floata,b,c;scanf("%f%f",&a,&b);c=add(a,b);printf("sumis%f\n",c);}floatadd(floatx,floaty)/*定义add函数,返回值类型是float型*/{doublez;z=sqrt(x)+sqrt(y);/*相当于z=sqrt((double)x)+sqrt((double)y)*/

return(z);/*相当于return(float)z;,因为返回值类型为float型*/

}运行输入1636<回车>输出结果为:

sumis10.000000第三十页,共八十三页,编辑于2023年,星期五2023/5/23307-4函数的调用

C语言规定,在以下几种情况下可以不用在调用函数前对被调用函数作原型声明:

(1)如果函数的值(函数的返回值)是整型或字符型,可以不必进行声明,系统对它们自动按整型声明。

第三十一页,共八十三页,编辑于2023年,星期五2023/5/23317-4函数的调用

(2)如果被调用函数的定义出现在主调函数之前,可以不必加以声明,因为编译系统已经先知道了已定义的函数,会自动处理的。

(3)如果已在所有函数定义之前,在文件的开头,在函数的外部已声明了函数原型,则在各个主调函数中不必对所调用的函数再作原型声明。第三十二页,共八十三页,编辑于2023年,星期五2023/5/23327-4函数的调用

(4)ANSIC标准在对函数进行类型声明时,除了规定要声明函数的类型外,还要求声明参数个数与参数类型,这是为了在编译时检查函数的调用是否合法(如实参的个数和类型与形参是否一致)。这样的声明称为用函数原型作声明。函数原型的一般形式是:函数类型函数名(参数类型l参数名1,参数类型2参数名2…);这种形式就是把定义函数时的函数首部(即第一行)搬过来加一个分号即可。第三十三页,共八十三页,编辑于2023年,星期五2023/5/23337-4函数的调用

7-4-4程序举例【例7-6】阅读下面程序,判断程序能否交换主函数中a和b的值。

main(){inta=1,b=2;swap(a,b);/*a、b是实参*/printf("实参:a=%d,b=%d\n",a,b);/*输出实参a、b的值*/}swap(inta,intb)/*a,b是形参,开辟与实参a、b不同的存储单元*/{intc;c=a;a=b;b=c;/*交换形参a、b中的值*/printf("形参:a=%d,b=%d\n",a,b);/*输出形参a、b的值*/}/*调用完毕,释放a、b、c所占存储单元*/运行结果是:形参:a=2,b=1

实参:a=1,b=2第三十四页,共八十三页,编辑于2023年,星期五2023/5/23347-4函数的调用【例7-7】由键盘输人三个整数分别给变量a、b、c,输出其中绝对值最大的数。#include<stdio.h>#include<math.h>main(){inta,b,c,max;scanf("%d,%d,%d",&a,&b,&c);max=find(a,b,c);/*编写主调函数时,可以不必具体考虑如何编写被调函数,只要知道返回值是三个数中绝对值最大者即可*/printf("max=%d\n",max);}intfind(intx,inty,intz){intmax;max=x;if(abs(max)<abs(y))max=y;if(abs(max)<abs(z))max=z;returnmax;}运行结果:

123,345,91<回车>max=345第三十五页,共八十三页,编辑于2023年,星期五2023/5/23357-4函数的调用【例7-8】求100~200之间的全部素数之和。#include<math.h>main(){intm,s=0;for(m=101;m<200;m=m+2)if(prime(m))s+=m;printf("s=%d\n",s);}intprime(intm){intk,j;k=sqrt(m);for(j=2;j<=k;j++)if(m%j==0)return0;return1;}运行结果如下:

s=3167第三十六页,共八十三页,编辑于2023年,星期五2023/5/23367-5函数的嵌套调用C语言程序不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又可以调用另一个函数main函数a函数b函数调用a函数调用b函数结束123456789第三十七页,共八十三页,编辑于2023年,星期五2023/5/23377-5函数的嵌套调用【例7-9】求1!+2!+3!+4!+…+20!main(){floatsum(int);/*函数sum的原型声明*/floatadd;add=sum(20);/*主函数调用sum函数,求20以内的自然数的阶乘和*/printf("add=%e",add);/*由于20以内的自然数的阶乘和超过int型和long型能表示的数的范围,所以用e格式输出结果*/}floatsum(intn)/*求n以内的自然数的阶乘和*/{floatfac(int);/*函数fac的原型声明*/intk;floats=0;for(k=1;k<=n;k++)s+=fac(k);/*调用fac求k!,并累加到s中*/returns;/*返回n以内的自然数的阶乘和*/}floatfac(intk)/*求k的阶乘值*/{floatt=1;intn;for(n=1;n<=k;n++)t*=n;returnt;/*返回k的阶乘值*/}运行结果为:

s=2.56133e+18第三十八页,共八十三页,编辑于2023年,星期五2023/5/23387-6函数的递归调用在C语言程序中,有时可以看到一个函数直接或间接地调用自身的现象,这种现象就是函数的递归调用。递归调用有两种方式:直接递归调用和间接递归调用。f函数调用f函数图7-2f1函数f2函数调用f2函数调用f1函数图7-3第三十九页,共八十三页,编辑于2023年,星期五2023/5/23397-6函数的递归调用【例7-10】用递归调用的方法求n!。

1当n=0或1时n!=n*(n-1)当n>1时

intfac(intn){intc;if(n==0||n==1)c=1;/*n的值为0或1时,n!为1*/elsec=n*fac(n-1);/*n>l时,调用本函数计算n!*/returnc;/*c中存放的是n!,返回值也是n!*/}main(){intn;scanf("%d",&n);if(n<0)printf("Dataerror!\n");elseprintf("%d!=%d\n",n,fac(n));}

运行结果如下:

4<回车>4!=24第四十页,共八十三页,编辑于2023年,星期五2023/5/23407-6函数的递归调用递归不是“循环定义”,任何递归定义必须满足如下条件:(1)被定义项在定义中的应用(即作为定义项的出现)具有更小的“尺度”,即需要求解的问题可以分解为一个相对简单的子问题。(2)被定义项在最小“尺度”上的定义不是递归的,即最终有一个子问题不是递归,必须有确定的值。第四十一页,共八十三页,编辑于2023年,星期五2023/5/23417-6函数的递归调用【例7-11】编程求Fibonacci数列的第n项。Fibonacci数列定义如下:

1当n=1时F(n)=1当n=2时

F(n-1)+F(n-2)当n>2时longfibo(int);main(){longf;intn;scanf("%d",&n);f=fibo(n);printf("%ld\n",f);}longfibo(intn){longf;if(n==1||n==2)f=1L;elsef=fibo(n-1)+fibo(n-2);returnf;}运行时结果:8<回车>21第四十二页,共八十三页,编辑于2023年,星期五2023/5/23427-7局部变量和全局变量

-7-1局部变量在一个函数或复合语句内部定义的变量是内部变量,它只在本函数或复合语句范围内有效,也就是说只能在本函数或复合语句内才能使用它们,这样的变量称为“局部变量”。第四十三页,共八十三页,编辑于2023年,星期五2023/5/23437-7局部变量和全局变量

floatf1(inta)/*函数f1*/{intb,c;……}/*在此函数内a,b,c有效,它们不是主函数中的a,b,c*/main(){inta,b;{intc=0;c=a+b;/*c只在复合语句内有效*/}……}/*在主函数内,a,b一直有效*/第四十四页,共八十三页,编辑于2023年,星期五2023/5/23447-7局部变量和全局变量

【例7-12】写出下面程序的运行结果。

main(){intk,a=0;for(k=1;k<=2;k++){inta=1;/*在复合语句内开辟新的a,故上面的a不起作用*/a++;printf("k=%d,a=%d\n",k,a);}/*释放复合语句内开辟的a,不能再使用它*/printf("k=%d,a=%d\n",k,a);/*a是主函数第一条语句中的a*/}运行结果是:

k=l,a=2k=2,a=2k=3,a=0第四十五页,共八十三页,编辑于2023年,星期五2023/5/23457-7局部变量和全局变量

7-7-2全局变量在函数之外定义的变量称为外部变量,外部变量是全局变量。全局变量可以为本文件中其它函数所共用,它的有效范围为:从定义变量的位置开始到本源文件结束第四十六页,共八十三页,编辑于2023年,星期五2023/5/23467-7局部变量和全局变量

inta=l,b=2;/*a,b为全局变量,以下三个函数都可以使用*/floatfl(inta)/*定义函数fl*/{intb,c;……}/*a,b,c只在本函数中使用*/intc=0,d=2;/*c,d为全局变量,以下两个函数可以使用它们*/f2(intx,inty)/*定义函数f2*/{inti,j;……}/*x,y,i,j只在本函数内有效*/main(){intm,n;/*m,n只在主函数内有效*/……}第四十七页,共八十三页,编辑于2023年,星期五2023/5/23477-7局部变量和全局变量

7-7-2全局变量(1)设全局变量的作用是:增加函数间数据联系的渠道。由于同一文件中的所有函数都能引用全局变量的值,因此,如果在一个函数中改变了全局变量的值,就能影响到其它函数,相当于各个函数间有直接的传递通道。由于函数的调用只能带回一个返回值,因此有时可以利用全局变量增加与函数联系的渠道,从函数得到一个以上的返回值。第四十八页,共八十三页,编辑于2023年,星期五2023/5/23487-7局部变量和全局变量7-7-2全局变量

(2)建议不在必要时不要使用全局变量,因为:①全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元;②它使函数的通用性降低了,因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去,但若该外部变量与其它文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性;③使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量的值。在各个函数执行时都可能改变外部变量的值,程序容易出错,因此要限制使用全局变量。第四十九页,共八十三页,编辑于2023年,星期五2023/5/23497-7局部变量和全局变量7-7-2全局变量

(3)如果外部变量在文件开头定义,则在整个文件范围内都可以使用该外部变量,如果不在文件开头定义,按上面规定作用范围只限于定义点到文件终点。如果在定义点之前的函数想引用该外部变量,则应该在该函数中用关键字extern作“外部变量声明”。这声明表示这些变量是在该函数后面定义的外部变量。在函数内部,从extern声明之处起,可以使用它们。第五十页,共八十三页,编辑于2023年,星期五2023/5/23507-7局部变量和全局变量【例7-13】程序示例。intmax(intx,inty)/*定义max函数*/{intz;z=x>y?x:y;return(z);}main(){externinta,b;/*外部变量声明,不重新开辟内存空间*/printf("%d",max(a,b));}inta=20,b=7;运行结果:

20第五十一页,共八十三页,编辑于2023年,星期五2023/5/23517-7局部变量和全局变量7-7-2全局变量(4)如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。第五十二页,共八十三页,编辑于2023年,星期五2023/5/23527-7局部变量和全局变量【例7-14】inta=1;/*定义全局变量a*/f(){inta=2;printf("f:a=%d\n",a);/*f中的局部变量a起作用*/}g(){printf("g:a=%d\n",a);/*全局变量a起作用*/}main(){inta=3;printf("main:a=%d\n",a);/*main中的局部变量a起作用*/f();g();}运行时输出:main:a=3f:a=2g:a=1第五十三页,共八十三页,编辑于2023年,星期五2023/5/23537-8动态存储变量与静态存储变量7-8-1变量的存储类别从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。从变量值存在的时间(即生存期)来分,可以分为静态存储变量和动态存储变量。所谓静态存储方式是指在程序运行期间分配固定的存储空间的方式,而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。第五十四页,共八十三页,编辑于2023年,星期五2023/5/23547-8动态存储变量与静态存储变量7-8-1变量的存储类别内存中的供用户使用的存储空间可以分为三部分:(1)程序区(2)静态存储区(3)动态存储区数据分别存放在静态存储区和动态存储区中。全局变量存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地分配和释放的。第五十五页,共八十三页,编辑于2023年,星期五2023/5/23557-8动态存储变量与静态存储变量7-8-1变量的存储类别在动态存储区中存放以下数据:①函数形参变量,在调用函数时给形参变量分配存储空间;②局部变量;③函数调用时的现场保护和返回地址等。对以上这些数据,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。第五十六页,共八十三页,编辑于2023年,星期五2023/5/23567-8动态存储变量与静态存储变量7-8-1变量的存储类别在C语言中每一个变量和函数有两个属性:数据类型和数据的存储类别。存储方法分为两大类:静态存储类和动态存储类。具体包含四种:自动的(auto),静态的(static),寄存器的(register),外部的(extern)。第五十七页,共八十三页,编辑于2023年,星期五2023/5/23577-8动态存储变量与静态存储变量7-8-2局部变量的存储方式(1)函数中的局部变量,如不做专门的声明,都是动态分配存储空间的,存储在动态存储区中,对它们分配和释放存储空间的工作是由编译系统自动处理的,因此这类局部变量称为局部动态变量或称自动变量。自动变量用关键字auto作存储类型的声明。“auto”也可以省略,auto不写则隐含确定为“自动存储类别”,它属于动态存储类别。第五十八页,共八十三页,编辑于2023年,星期五2023/5/23587-8动态存储变量与静态存储变量7-8-2局部变量的存储方式(2)有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次调用该函数时,该变量已有上一次函数调用结束时保留下来的值。为此,应该指定该局部变量为“局部静态变量”,用static加以声明。第五十九页,共八十三页,编辑于2023年,星期五2023/5/23597-8动态存储变量与静态存储变量7-8-2局部变量的存储方式【例7-15】写出下面程序的运行结果。voidf(intc){inta=0;/*每次调用时都先赋0,不保留上一次的值*/staticintb=0;/*第一次调用时初值为0,下次调用时保留上一次的值*/a++;b++;printf("%d:a=%d,b=%d\n",c,a,b);}main(){intk;for(k=1;k<=2;k++)f(k);/*调用两次函数*/}运行结果是:

1:a=1,b=12:a=1,b=2第六十页,共八十三页,编辑于2023年,星期五2023/5/23607-8动态存储变量与静态存储变量7-8-2局部变量的存储方式对局部静态变量的说明:①局部静态变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即局部动态变量)属于动态存储类别,占动态存储区空间而不占固定空间,函数调用结束后即释放。第六十一页,共八十三页,编辑于2023年,星期五2023/5/23617-8动态存储变量与静态存储变量7-8-2局部变量的存储方式②局部静态变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值,以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而对自动变量赋初值,不是在编译时进行的,而在函数调用时进行,每调用一次函数重新赋一次初值。第六十二页,共八十三页,编辑于2023年,星期五2023/5/23627-8动态存储变量与静态存储变量7-8-2局部变量的存储方式③如在定义局部变量时不赋初值的话,则对静态变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量),而对自动变量来说,它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新分配另一存储单元,因此所分配的单元中的值是不确定的。以上结论对数组的初始化也成立。第六十三页,共八十三页,编辑于2023年,星期五2023/5/23637-8动态存储变量与静态存储变量7-8-2局部变量的存储方式【例7-16】写出以下程序的运行结果。main(){intk,a[5];for(k=0;k<5;k++)printf("%d",a[k]);/*a数组没有赋值,输出一行随机数*/printf("\n");printf("Firsttime:\n");fun();/*第一次调用时,为数组a的全部元素自动赋0*/printf("Secondtime:\n");fun();/*第二次调用时,保留数组a的原值*/}fun()/*由于定义为静态变量,两次调用的结果不同*/{inti;staticinta[5];for(i=0;i<5;i++)printf("%d",a[i]);printf("\n");a[0]++;/*改变a[0]的值*/}运行结果是:**************

Firsttime:00000Secondtime:10000(其中**************代表随机数)

第六十四页,共八十三页,编辑于2023年,星期五2023/5/23647-8动态存储变量与静态存储变量7-8-2局部变量的存储方式④虽然局部静态变量在函数调用结束后仍然占存储单元,但由于该变量是局部变量,其它函数不能引用它。第六十五页,共八十三页,编辑于2023年,星期五2023/5/23657-8动态存储变量与静态存储变量7-8-2局部变量的存储方式应该看到:用静态存储要多占内存(长期占用不释放,而不能像动态存储那样一个存储单元可供多个变量使用,节约内存),而且降低了程序的可读性,当调用次数多时往往弄不清局部静态变量的当前值是什么。因此,如不必要,不要多用局部静态变量。第六十六页,共八十三页,编辑于2023年,星期五2023/5/23667-8动态存储变量与静态存储变量7-8-2局部变量的存储方式(3)为了减少从内存中存取变量值的时间,以提高执行效率,C语言允许将局部变量的值放在运算器中的寄存器中,需要时直接从寄存器取出。这种变量叫“寄存器变量”,用关键字register作声明。第六十七页,共八十三页,编辑于2023年,星期五2023/5/23677-8动态存储变量与静态存储变量7-8-2局部变量的存储方式【例7-17】输出1到5的阶乘值。

intfac(intn){registerinti,f=1;

/*i和f使用频繁,因此定义为寄存器变量*/

for(i=1;i<=n;i++)f=f*i;return(f);}main(){inti;for(i=1;i<=5;i++)printf("%d!=%d\n",i,fac(i));}运行结果为:1!=12!=23!=64!=245!=120第六十八页,共八十三页,编辑于2023年,星期五2023/5/23687-8动态存储变量与静态存储变量7-8-2局部变量的存储方式①只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量)不行。在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束释放寄存器。此后,在调用另一个函数时又可以利用它来存放该函数的寄存器变量。第六十九页,共八十三页,编辑于2023年,星期五2023/5/23697-8动态存储变量与静态存储变量7-8-2局部变量的存储方式②一个计算机系统中的寄存器数目是有限的,不能定义任意多个寄存器变量。不同的系统允许使用的寄存器个数不同。如果没有足够的寄存器来存放指定的变量,将自动按自动变量来处理。寄存器变量的声明应尽量靠近使用它的地方,用完之后尽快释放。第七十页,共八十三页,编辑于2023年,星期五2023/5/23707-8动态存储变量与静态存储变量7-8-2局部变量的存储方式③局部静态变量不能定义为寄存器变量,不能写成:registerstaticinta,b,c;不能把变量a,b,c既放在静态存储区中,又放在寄存器中,二者只能居其一。④不能对寄存器变量进行求地址运算,因为寄存器变量的值不是存放在内存中。第七十一页,共八十三页,编辑于2023年,星期五2023/5/23717-8动态存储变量与静态存储变量7-8-3全局变量的存储方式全局变量是在函数的外部定义的,编译时分配在静态存储区。全局变量可以为程序中各个函数所引用。第七十二页,共八十三页,编辑于2023年,星期五2023/5/23727-8动态存储变量与静态存储变量7-8-3全局变量的存储方式(1)允许其它文件中的函数引用。如果在一个文件中要引用在另一文件中定义的全局变量,应该在需要引用它的文件中用extern作声明。

第七十三页,共八十三页,编辑于2023年,星期五2023/5/23737-8动态存储变量与静态存储变量7-8-3全局变量的存储方式(2)只被本文件中的函数引用。有时在程序设计中希望某些全局变量只限于被本文件引用而不能被其它文件引用,这时可以在定义外部变量时前面加一个static声明。加上static声明、只能用于本文件的外部变量(全局变量)称为静态外部变量。

第七十四页,共八十三页,编辑于2023年,星期五2023/5/23747-8动态存储变量与静态存储变量7-8-4存储类别小结1.共有四种存储类别:

static(声明静态内部变量或外部静态变量)

auto(声明自动局部变量)

register(声明寄存器变量)extern(声明变量是已定义的外部变量)第七十五页,共八十三页,编辑于2023年,星期五2023/5/23757-8动态存储变量与静态存储变量7-8-4存储类别小结

2.从作用域分,有全局变量和局部变量。它们可采取的存储类别为:局部变量:自动变量,即动态局部变量(离开函数,值就消失);静态局部变量(离开函数,值仍保留);寄存器变量(离开函数,值就消失);形式参数

温馨提示

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

评论

0/150

提交评论