(1.7)--第7章 函数C语言程序设计.ppt
第第7 7章章 函数函数1 “米奇妙妙屋”是由“积木”搭建而成,一个C语言“程序”由若干个称之为“函数”的模块构成,函数与程序的关系如同“积木”与“米奇妙妙屋”,因此C语言被称为“函数式的语言”。要用“函数积木”搭好“程序米奇妙妙屋”,就要先学会函数的定义、声明和调用等函数的相关知识。学习目标:21.会进行函数的格式定义、声明2.会包含库函数的头文件、正确调用已有函数3.理解函数参数的含义、传递过程、传递的值4.理解函数的递归过程、什么情况适合用递归解决5.理解函数如何返回结果,有几种方式6.变量的有效范围的确定6.变量的生存周期的确定本章重点内容:函数式多文件程序结构1变量的作用域、变量的存储类型5函数的定义、调用及声明24函数的嵌套与递归3函数的参数传递过程3函数1函数2函数m文件1文件2文件n程序C程序是函数的集合体,每个函数是一个独立的程序模块;一个C程序有且仅有一个主函数,若干个子函数,程序总是从主函数开始执行,在主函数内结束;函数可集中或分散存放在一个或多个源程序文件中;所有子函数地位平等,可互相调用、自我调用,但一个函数内部不能嵌套定义另一个函数。intmain()intmain()intInsert()intInsert()intQuery()intQuery()intUpdate()intUpdate()intDelete()intDelete()intList()intList()intSave()intSave()intLoad()intLoad()f1()main()f11()f2()f12()f21()47.1函数式多文件程序结构思考思考1:为什么采用多函数结构?为什么采用多函数结构?请看如下示例:主函数内代码显得非常重复、累赘!57.1函数式多文件程序结构功能为打印i行,j列的星号矩阵定义函数调用函数,打印3行2列调用函数,打印1行6列调用函数,打印4行3列6显然,主函数内代码简单、明了!7.1函数式多文件程序结构函数的作用函数的作用:在程序设计中,在程序设计中,采用采用“自顶向下,逐步求精自顶向下,逐步求精”的思想,将问题的思想,将问题逐逐步分解步分解成若干个独立的成若干个独立的模块模块,每个模块对应个一个,每个模块对应个一个函数函数,如果有相,如果有相应的库函数就使用库函数,没有就自定义函数。应的库函数就使用库函数,没有就自定义函数。函数功能独立函数功能独立可重复调用可重复调用,能提高代码的利用率能提高代码的利用率;函数函数可相互调用可相互调用“组装组装”成程序,成程序,使程序结构清晰,层次分明使程序结构清晰,层次分明;库函数是黑盒子,库函数是黑盒子,不必知道如何设计,不必知道如何设计,只需知道如何使用只需知道如何使用。77.1函数式多文件程序结构思考2:为什么采用多文件结构?当一个文件的代码改变时,只需重新编译该文件不必重新编译全部程序,缩短了编译时间。C语言是以文件为单位进行编译。C语言允许将一个源程序分成若干个文件分别编译、调试,一旦所有文件编译完毕,就可以将它们链接起来,形成完整的目标程序。1 12 23 31 11 12 2+87.1函数式多文件程序结构7.2函数的定义、调用及声明7.2.17.2.1函数函数分类分类 在C语言中,从函数定义的角度看,函数可分为库函数和用户自定义函数两种。(一)(一)库函数库函数 一组由编译系统提供的预先设计并编译好的用来实现各种通用或常用的功能的函数,这些函数根据功能不同被划分在不同的函数库中,称为库函数。详见附录C库函数。例如:printf,scanf,sqrt,pow等(二)(二)用户自定义函数用户自定义函数 用户按需要自行编写具有特定功能的函数,称为用户自定义函数。有无参函数、有参函数两种形式。97.2.27.2.2函数的定义函数的定义 函数类型 函数名(形式参数类型 形式参数名)语句组 花括号“”中包含的声明和功能语句,函数的功能是功能语句来实现。不能省略!形式参数列表函数首部:函数首部:函数类型:函数运算结果的类型(数据类型标识符)。若函数不需要返回值,可指定函数类型为void。函数类型为int时可省略。函数名:用于标记函数的标识符。一个程序中不允许出现同名的函数,函数名不可省略!形式参数列表:用来说明形式参数的类型和名称,()不能省略!7.2函数的定义、调用及声明10函数体:函数体:117.2函数的定义、调用及声明1.有参函数例1:编程实现3!5!8!t=t*j;j+;s=s+t;s=0;j=3真假t=1;j=1;输出s;假s=s+t;j=8t=t*j;j+;真t=1;j=1;假j=5t=t*j;j+;真t=1;j=1;s=s+t;7.2函数的定义、调用及声明求n的阶乘函数名函数体函数返回形式参数名形式参数类型函数类型int fact(intn)intj,t=1;for(j=1;jb?max=a;max=b;maxn)returnm;elsereturnn;求两个数中的大数。137.2函数的定义、调用及声明 例3:用程序实现钻石图案的输出1.有参函数voidprintstar(intn)inti;for(i=1;i=4-n;i+)printf();for(i=1;i=2*n-1;i+)printf(*);printf(n);输出行中空格和*n=1n0输出4-n个空格输出2n-1个*假真开始结束14n+n-2.无参函数7.2函数的定义、调用及声明void disp1(void)printf(*n);printf(*n);printf(*n);printf(*n);printf(*n);void disp0()printf(*n);printf(*n);printf(*n);printf(*n);printf(*n);void disp2()printf(*n);printf(*n);printf(*n);printf(*n);printf(*n);void disp3()printf(*n);printf(*n);printf(*n);printf(*n);printf(*n);157.2函数的定义、调用及声明函数定义时应注意的问题:1.形参类型必须一对一声明函数的定义是独立的,不允许在一个函数体内再定义另一个函数。void类型函数无需返回值,其余函数类型都必须通过return返回函数的运算结果。不论函数的形式参数类型是否相同,都必须一对一声明。如:(float a,float b)不能写成(float a,b)。2.函数的不允许嵌套定义3.return与函数类型的对应16 一个C程序可以包含多个函数,但必必须须包包含含且且只只能能包包含含一一个个main()main()函函数数。程序的执行从main()函数开始,到main()函数结束。程序中的其它函数必须通过main()函数直接或者间接地调用才能执行。函数调用的过程就是主函数执行到调用语句时把实参的值一一传递给对应形参,流程转向被调用函数,执行函数体,执行完毕,流程返回调用处,如有返回值,则带回返回值,主函数继续执行的过程。7.2函数的定义、调用及声明7.2.37.2.3函数的调用函数的调用1.函数调用过程177.2函数的定义、调用及声明函数的调用与返回/*1*/*2*/*3*/*4*/*5*/*11*/*12*/*13*/*14*/*15*/*16*/main()main()int a,b,c;int a,b,c;scanf(%d,%d,&a,&b);scanf(%d,%d,&a,&b);c=c=max(a,b);max(a,b);printf(max=%dn,c);printf(max=%dn,c);int int max(int x,int y)max(int x,int y)int z;int z;if(xy)z=x;if(xy)z=x;else z=y;else z=y;return(z);return(z);保护断点和保护断点和现场,转向现场,转向1111入口入口虚实结合虚实结合a-xa-x,b-yb-y返回断点返回断点恢复现场恢复现场带回函数值带回函数值断点断点18#inclueintfact(intn)intj,t=1;for(j=1;j=n;j+)t*=j;returnt;voidmain()ints=0;s=fact(3)+fact(5)+fact(8);printf(“3!+5!+8!=%dn”,s);7.2.37.2.3函数的调用函数的调用p表达式形式遵循先定义后调用的原则适用于非void类型的函数,函数的返回值参与主调函数中表达式的运算197.2函数的定义、调用及声明2.函数调用形式#includedoublemax(doublem,doublen)if(mn)returnm;elsereturnn;voidmain()doublea,b,c;scanf(“%f%f%f”,&a,&b,&c);printf(“max=%f”,max(max(a,b),c);7.2.37.2.3函数的调用函数的调用p参数形式适用于非void类型的函数,函数的返回值在主调函数中作自己或其它函数的实参。207.2函数的定义、调用及声明1#includevoidprintstar(intn)inti;for(i=1;i=4-n;i+)printf();for(i=1;i=2*n-1;i+)printf(*);printf(n);voidmain()inti;for(i=1;i=1;i-)printstar(i);7.2.37.2.3函数的调用函数的调用p语句形式21主要用于void类型的函数非void类型的函数采用语句调用形式将不接受函数的返回值7.2函数的定义、调用及声明7.2函数的定义、调用及声明7.2.47.2.4函数类型与函数的返回值函数类型与函数的返回值 通过return语句返回主调函数的返回值类型应该和函数的类型一致。如果不一致,则返回值类型应转换为函数类型。#include void main()float a=1.5,b=0.5,c;c=max(a,b);printf(“max is%fn”,c);max(float x,float y)float z;z=xy?x:y;return z;运行结果为运行结果为:函数返回值类型缺省了,所以编译器默认为是int,而return语句后的表达式z的值为float,系统自动进行转换,故丢失精度了!原因分析:原因分析:227.2.57.2.5函数的声明函数的声明函数遵循先定义后使用的原则,若先使用后定义必须前向声明。#include float max(float x,float y)float z;z=xy?x:y;return z;void main()float a=1.5,b=0.5,c;c=max(a,b);printf(“max is%fn”,c);#include float max(float x,float y)float z;z=xy?x:y;return z;先定义,后使用先声明,后使用void main()float a=1.5,b=0.5,c;c=max(a,b);printf(“max is%fn”,c);float max(float x,float y);237.2函数的定义、调用及声明7.2.57.2.5函数的声明函数的声明 函数声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便编译系统识别并检查调用是否合法。u函数声明的形式(1)函数类型函数名(参数类型1,参数名1,参数类型n,参数名n);(2)函数类型函数名(参数类型1,参数类型n,);u函数声明的位置u函数声明的作用主调函数的声明区或所有函数之外的声明区。成熟型稳健型247.2函数的定义、调用及声明若函数返值是char或int型,系统自动按int型处理,无需声明。u函数声明的特例 float max(float x,float y);float max(float,float);7.3函数的参数传递7.3.17.3.1函数间数据传递函数间数据传递u参数:通过形式参数和实际参数u返回值:用return语句返回计算结果在不同的函数之间传递数据,可以使用的方法:形式参数:定义一个有参函数时,函数名后()中的参数称为形式参数,简称形参。例如:int max(int x,int y);调用一个有参函数时,函数名后()中的参数称为实际参数,简称实参。例如:c=max(2,7*5);257.3函数的参数传递7.3.27.3.2函数的参数传递数值函数的参数传递数值 发生函数调用时,根据实参和形参的对应关系,将实参的值单向地传递给形参,供被调函数在执行时使用。在函数执行过程中,对形参做任何修改都不影响实参值。main funcaxby调用函数时3434 main funcaxby调用结束3437定义函数func()intfunc(intx,inty)y=x+y;return(y);在main()函数中执行赋值语句 m=func(a,b);假设主函数中a=3,b=4,如图 形参实参2617.3函数的参数传递main()函数 int a,b;a=5;b=10;swap(a,b);swap(int x,int y)函数 int temp temp=x;语句 x=y;语句 y=temp;语句 510实参变量a实参变量b形参变量x形参变量y复制复制temp=xx=yy=tempswap函数的执行过程和各个变量的变化过程未改变10510调用swap函数执行swap函数执行swap函数执行swap函数5变量temp5277.3函数的参数传递7.3.27.3.2函数的参数传递地址函数的参数传递地址 当形参和实参是数组名时,实参传递给形参的是数组的首地址,形参取得实参的值(地址)后与实参共享同一个数组。voidbubble_sort(intp,intn)inti,j,temp;for(i=0;in-1;i+)for(j=0;jpj+1)temp=pj;pj=pj+1;pj+1=temp;voidmain()inti;inta10=3,5,-6,88,34,76,54,28,22,75;printf(排序前:n);for(i=0;i10;i+)printf(“%d”,ai);printf(“n排序后:n);bubble_sort(a,10);for(i=0;ip2temp=p1;p1=p2;p2=temp;3-63488765488882275p3p4temp=p3;p3=p4;p4=temp;p4p5temp=p4;p4=p5;p5=temp;p5p6temp=p5;p5=p6;p6=temp;p6p7temp=p6;p6=p7;p7=temp;p7p8temp=p7;p7=p8;p8=temp;p8p9temp=p8;p8=p9;p9=temp;p0p1temp=p0;p0=p1;p1=temp;p4p5temp=p4;p4=p5;p5=temp;p5p6temp=p5;p5=p6;p6=temp;76p6p7temp=p6;p6=p7;p7=temp;22767675p7p8temp=p7;p7=p8;p8=temp;p4p5temp=p4;p4=p5;p5=temp;2854p5p6temp=p5;p5=p6;p6=temp;542275p3p4temp=p3;p3=p4;p4=temp;2834p4p5temp=p4;p4=p5;p5=temp;342254p3p4temp=p3;p3=p4;p4=temp;2828222253-6297.3函数的参数传递在函数调用时,实参的值应一一对应地传递给形参,实参与形参的个数应相同,类型应兼容。7.3函数的参数传递7.3.27.3.2函数的参数传递小结函数的参数传递小结形参只有所在函数被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后,不能再使用该形参。30除了用数组名作为函数形参来实现参数的地址传递以外,还有一种更广的应用那就是用指针变量来作为函数的形参实现地址传递方法,7.4.17.4.1函数的嵌套调用函数的嵌套调用 C语言中,函数之间都是平行的,可以在一个函数中调用另一个函数。当被调函数的定义中又包含了对第三个函数的调用,这就构成了函数的嵌套调用。main main 函数函数 a()a()函数函数 b()b()函数函数调用调用a()a()函数函数 调用调用b()b()函数函数 结束结束执行执行调用调用返回返回返回返回C语言允许函数嵌套调用,不允许函数嵌套定义。调用调用7.4函数的嵌套与递归3117.4函数的嵌套与递归32intdif(intx,inty,intz)return(max(x,y,z)min(x,y,z);intmax(intx,inty,intz)intr;r=xy?x:y;return(rz?r:z);intmin(intx,inty,intz)intr;r=xy?x:y;return(rz?r:z);【例例例例】计算三个数中最大数与最小数的差。计算三个数中最大数与最小数的差。#includeintdif(intx,inty,intz);intmax(intx,inty,intz);intmin(intx,inty,intz);voidmain()inta,b,c,d;scanf(%d%d%d,&a,&b,&c);d=dif(a,b,c);printf(Max-Min=%dn,d);main()main()调用函数dif输出结束difdif函数函数max函数函数调用函数max调用函数minmin函数函数7.4函数的嵌套与递归337.4.27.4.2函数的函数的递归递归调用调用如果一个函数在调用的过程中出现直接或者间接地调用该函数本身,称为函数的递归调用。int fn(int a)int x,y;y=fn(x);return(3*y);函数fn()直接调用自身,称为直接递归调用。7.4函数的嵌套与递归34 如果在f1()函数执行过程中要调用 f2()函数,而在f2()函数执行过程中又要调用f1()函数,这种情况称为函数的间接递归调用。fn函数函数调用调用fn函数函数a直接递归调用直接递归调用b间接递归调用间接递归调用f1f1函数函数调用调用f2f2函数函数f2f2函数函数调用调用f1f1函数函数7.4.27.4.2函数的函数的递归递归调用调用7.4函数的嵌套与递归35问题:求自然数n的阶乘。用递归方法求n!,对应的递推公式为:n!=1(n=0,1)n!=n(n-1)!(n1)7.4.27.4.2函数的函数的递归递归调用调用归归回回4!=43!3!=32!2!=21!1!=12!=21=23!=32=64!=46=24递递推推递归是通过递推过程找到实际问题与已知解(递推结束条件)之间的联系,通过回归解决实际问题。7.4函数的嵌套与递归36main()intn,p;printf(N=?);scanf(%d,&n);p=facto(n);printf(%d!=%dn,n,p);intfacto(intn)intr;if(n=1)r=1;elser=n*facto(n-1);/*递归调用*/return(r);7.4.27.4.2函数的函数的递归递归调用调用程序代码为:程序代码为:已知:n阶乘的递归定义为:n!=1 当 n=1 时n!=n*(n-1)!当 n 1 时7.4函数的嵌套与递归37主函数 第一次调用 第二次 第三次 第四次 n=4 p=facto(4)调用 n=4 r=4*facto(3)调用 n=3 r=3*facto(2)调用n=2 r=2*facto(1)n=1 return(1)返回 r=2*1=2 return(2)返回 r=3*2=6 return(6)返回 r=4*6=24 return(24)返回 p=24 打印 247.4.27.4.2函数的函数的递归递归调用调用int facto(int n)facto(int n)int int r;if if(n=1)(n=1)r=1;=1;else else r=n*facto(n-1);facto(n-1);return(return(r););int int facto(int n)facto(int n)int int r r;if(n=if(n=1 1)int facto(int n)facto(int n)int int r;if(n=0)if(n=0)r=1;=1;else else r=n*facto(n-1);facto(n-1);return(return(r););r r=n*n*facto(n-1)facto(n-1)int facto(int n)int r;if(n=1)int facto(int n)facto(int n)int int r;if(n=0)if(n=0)r=1;=1;else else r=n*facto(n-1);facto(n-1);return(s);return(s);r=n*facto(n-1)int facto(int n)int r;if(n=1)r=1;else r=n*facto(n-1);return(s);int int facto(int n)facto(int n)int int facto(int n)facto(int n)int int r r;int int r r;if(n=1)if(n=1)if(n=1)if(n=1)r r=n*n*facto(n-1)facto(n-1)r r=1 1return(return(1 1)r r=2 2*1 1return(return(2 2)return(6)r r=3 3*2 2r r=4 4*6 6return(return(24)24)1234321n=4n=3n=2n=17.4函数的嵌套与递归递归程序的执行过程递归程序的执行过程387.4函数的嵌套与递归p 这种转化能使问题越来越简单,越来越接近并最终达到某个已经有明确答案的问题,即递推的“出口”,从而结束递推。7.4.27.4.2函数的函数的递归递归调用调用当问题符合以下两个条件时,可以用递归法解决:p可以把要求解的问题转化为一个新的问题,这个新问题的解决办法与原来老问题相同,只是要求处理的对象(实际参数)的值有规律的递增或递减。397.4函数的嵌套与递归【例】反序输出整数n(n=0)。例如 n=12345,输出:54321。问题分析:1.若整数n为1位数字(0n9):则可以直接输出。2.将任意一个整数n(*)(n=10)分为两部分:个位()除个位以外的其余部分(*)3.将分解后的两部分分别看成整体,则解决的算法为:输出n的个位()反序输出n的除个位以外的其余部分(*)由 1 推出递推终止条件。由 3 得到递归算法。7.4.27.4.2函数的函数的递归递归调用调用407.4函数的嵌套与递归递归算法描述:若:整数 n 只有 1 位数字,则:输出该整数n;否则:输出n的个位;反序输出n的除个位以外的其余部分。程序:prtn(int n)/问题规模为n if(0=n&n y?x:y;z=x y?x:y;z=x y?x:y;return(z);return(z);return(z);void f2()void f2()void f2()printf(%dn,printf(%dn,printf(%dn,zzz);););局部变量局部变量局部变量局部变量 变量变量变量变量x x x x、y y y y、z z z z的作用域的作用域的作用域的作用域引用错误!引用错误!引用错误!引用错误!17.5变量的作用域与存储(2)主函数)主函数main()中定义的变量也是局部变量中定义的变量也是局部变量,它只能在主函数中,它只能在主函数中使用,其它函数不能使用。使用,其它函数不能使用。int f3(int x);int f3(int x);void main()void main()int int a a=2,=2,b b;b=a+y;b=a+y;printf(%dn,b);printf(%dn,b);int f3(int int f3(int x x)int int y y;y=a+5;y=a+5;return(y);return(y);局部变量局部变量局部变量局部变量 变量变量变量变量a a a a、b b b b的作用域的作用域的作用域的作用域变量变量变量变量x x x x、y y y y的的的的作用域作用域作用域作用域局部变量局部变量局部变量局部变量 错误!错误!错误!错误!错误!错误!错误!错误!44457.5变量的作用域与存储(3)在复合语句中定义的变量也是局部变量)在复合语句中定义的变量也是局部变量,其作用域只在复合语其作用域只在复合语其作用域只在复合语其作用域只在复合语句范围内。句范围内。句范围内。句范围内。#include void main()int a=2,b=4;int k,b;k=a+5;b=a*5;printf(k=%dn,k);printf(b=%dn,b);printf(b=%dn,b);a=k+2;mainmain中的局部变量中的局部变量 复合语句中的局部变量复合语句中的局部变量复合语句中的局部变量复合语句中的局部变量 mianmian中中中中变量变量变量变量a a、b b的作用域的作用域的作用域的作用域 复合语句中变量k、b的作用域输出输出输出输出k=7k=7 输出输出输出输出b=10b=10 输出输出输出输出b=4b=4 错误!错误!错误!错误!2.全局变量全局变量7.5变量的作用域与存储全局变量也称为外部变量,是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是从定义该变量的位置开始至整个源程序文件结束及有及有externextern说明说明的其它源文件。的其它源文件。46#include#include#include#include int sign();int sign();/计算数计算数计算数计算数n n的平方根的平方根的平方根的平方根float sqr()float sqr()if(n 0)if(n 0)return(sqrt(n);return(sqrt(n);else else return(-1);return(-1);float n=0;float n=0;void main()void main()int s;int s;float t;float t;scanf(%f,&n);scanf(%f,&n);s=sign(n);s=sign(n);/取符号取符号取符号取符号 t=sqr(n);t=sqr(n);/取平方根取平方根取平方根取平方根 printf(s=%d t=%f,s,t);printf(s=%d t=%f,s,t);int sign()int sign()int r=0;int r=0;if(n 0)r=1;if(n 0)r=1;if(n 0)r=-1;if(n 0)r=-1;return(r);return(r);局部变量局部变量局部变量局部变量全全全全局局局局变变变变量量量量n n的的的的作作作作用用用用域域域域 局部变局部变量量s、t的作用的作用域域局部变量局部变量局部变量局部变量局部变量局部变量r的作用域的作用域错误!错误!错误!错误!1(1)在函数中使用全局变量,一般应作全局变量说明。全局变量说明可以扩展变量的作用域。全局变量的说明符为 extern。但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。7.5变量的作用域与存储47void gx(),gy();void gx(),gy();void main()void main()externextern int x,y;int x,y;printf(1:x=%dty=%dn,x,y);printf(1:x=%dty=%dn,x,y);y=246;y=246;gx();gy();gx();gy();externextern int x,y;int x,y;void gx()void gx()x=135;x=135;printf(2:x=%dty=%dn,x,y);printf(2:x=%dty=%dn,x,y);int x=0,y=0;int x=0,y=0;void gy()void gy()printf(3:x=%dty=%dn,x,y);printf(3:x=%dty=%dn,x,y);全局变量定义全局变量定义全局变量定义全局变量定义未说明前未说明前未说明前未说明前的作用域的作用域的作用域的作用域全局变量说明全局变量说明全局变量说明全局变量说明说明后说明后说明后说明后的作用域的作用域的作用域的作用域全局变量说明全局变量说明说明后说明后说明后说明后的作用域的作用域的作用域的作用域运行结果:运行结果:1:x=0 y=0 2:x=135 y=246 3:x=135 y=2467.5变量的作用域与存储(2)若外部变量与局部变量同名,则外部变量被屏蔽若外部变量与局部变量同名,则外部变量被屏蔽。要引用全局变。要引用全局变量,则必须在变量名前家上两个冒号量,则必须在变量名前家上两个冒号“:”#include int a=10;/全局变量全局变量void main()int a=100;/局部变量(与全局变量同名)局部变量(与全局变量同名)printf(local a=%dn,a);printf(global a=%dn,:a);运行结果:运行结果:local a=100global a=10 注意:注意:局部变量与全局变量同名极易导致程序员局部变量与全局变量同名极易导致程序员犯逻辑错误。犯逻辑错误。(3)应尽量少使用全局变量。应尽量少使用全局变量。全局变量在程序全部执行过程中始终占用存储单元 降低了函数的独立性、通用性、可靠性及可移植性 降低程序清晰性,容易出错487.5变量的作用域与存储7.5.7.5.2 2变量的变量的存储存储491 1.程序的内存区域程序的内存区域 一个程序将操作系统分配给其运行的内存块分为4个区域:(1)代码区,存放程序的代码,即程序中各个代码块。(2)全局数据区,存放程序的全局数据和静态数据。(3)堆区,存放程序的动态数据。(4)栈区,存放程序的局部数据,各个函数中的数据。7.5变量的作用域与存储2 2.变量的存储方式与生存期变量的存储方式与生存期变量的存量的存储方式决定方式决定变量量存在的存在的时间(即生存期)(即生存期)。c语言中,每一个变量和函数有2个属性:数据类型和数据的存储类型。数据类型指数据在内存的大小,存储类型指的是数据在内存中的存储的方式。存储方式分为2大类:静态存储和动态存储。具体包含四种:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)。n变量说明的一般形式:变量说明的一般形式:存贮类型说明符 数据类型说明符 变量名称;507.5变量的作用域与存储(一)局部变量的存储(1)局部变量局部变量的动态存储的动态存储如不做专门的说明,都是动态分配存储空间的,存储在动态存储的栈区中。自动变量用auto关键字作说明,但auto可以省略。函数运行结束释放所占内存。例如:auto int b=3;与 int b=3 等价。当希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即占用的存储空间不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。就应该指定该局部变量为“局部静态变量”,用static加以说明,static型变量存储在全局存储区。(2)局部变量局部变量的静态存储的静态存储intf(inta)autointb=0;staticintc=3;b=b+1;c=c+1;return(a+b+c);voidmain()inta=2,j;for(i=0;i3;i+)printf(%d,f(a);运行结果为 7 8 951l 静态局部变量与自动变量均属于局部变量l 静态局部变量生存期长,为整个源程序。自动变量生存期短。l 静态局部变量的生存期虽然为整个源程序,但是其作用域仍与自动变量相同 l l 静态局部变量若在定义时未赋初值,则静态局部变量若在定义时未赋初值,则系统自动赋初系统自动赋初值值0l l 静态局部变量静态局部变量赋初值只一次,而自动变量赋初值可能,而自动变量赋初值可能多次多次7.5变量的作用域与存储如果一些变量使用特别频繁(如一个函数中执行10000次以上循环,每次循环都要用到某个局部变量),为了减少访问内存时间,提高效率,c语言允许将局部变量的值存于运算器的寄存器中。这种变量成为寄存器变量,用关键字register作说明。(二)(二)局部变量局部变量的寄存器存储的寄存器存储intfac(intn)registerinti,f=1;for(i=1;i=n;i+)f=f+1;returnf;voidmain()inti;for(i=1;i=5;i+)printf(“%d!=%dn”,i,fac(i);这里如果n的值很大,则能节约许多执行时间。527.5变量的作用域与存储(三)全部变量的存储全局变量在函数的外部定义,编译时分配在全局静态存储区。全局变量可以为程序中各个函数所引用。(1)允许其他文件中的函数引用。如果在一个文件中要引用在另一个文件中定义的全局变量,应该在需要引用的文件中,用extern作说明。file1.c:int a=5;main()int b=3,c;c=a*b;file2.c:extern int a;main()int y=3,z;z=y*a;printf(“%d”,z);(2)限定只被本文件中的函数引用。希望某些全局变量只限于被本文件引用而不能被其他文件引用。这时可以在定义外部变量时前面加一个static说明。file1.c:static int a;file2.c:extern int a;虽然在file2中使用了extern,但file2仍然无法使用file1中的全局变量a。53547.5变量的作用域与存储7.5.7.5.3 3小结小结作用域存储类型存储方式存储区域全局变量函数内部变量形式参数autostatic局部变量registerautostaticregisterstaticextern动态存储静态存储动态存储动态存储静态存储动态存储静态存储静态存储内存栈区全局数据区cpu寄存器内存栈区全局数据区cpu寄存器全局数据区全局数据区生存周期函数运行到结束程序编译到程序结束函数运行到结束函数运行到结束程序编译到程序结束函数运行到结束程序编译到程序结束程序编译到程序结束变量可以从作用域、生存周期、存放位置、生存期等方面划分: