C语言程序设计第8章(函数)_第1页
C语言程序设计第8章(函数)_第2页
C语言程序设计第8章(函数)_第3页
C语言程序设计第8章(函数)_第4页
C语言程序设计第8章(函数)_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

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

文档简介

1、第第8章章函函数数本章学习要点本章学习要点:q 掌握函数的定义方法掌握函数的定义方法;q 掌握函数的类型和返回值掌握函数的类型和返回值;q 掌握形式参数与实际参数、参数传递掌握形式参数与实际参数、参数传递;q 掌握函数的正确调用,了解函数的递归调用掌握函数的正确调用,了解函数的递归调用;q 掌握局部变量和全局变量、变量的存储类型,作用域掌握局部变量和全局变量、变量的存储类型,作用域和和 生存期生存期;8.1 概述概述函数函数:英文名为英文名为Function,直译为直译为“功能功能”,“函数函数”的意思的意思.在在C语言里语言里,函数指的是函数指的是实现一个特定功能的程序模块实现一个特定功能的

2、程序模块.它相当于其他它相当于其他语言中的子程序语言中的子程序.一个一个C语言程序可由一个语言程序可由一个主函数主函数和若干个函数构成和若干个函数构成,其中主函其中主函数是不可缺省的数是不可缺省的.每个每个C程序由主函数调用其他函数程序由主函数调用其他函数,其他函数也可其他函数也可以相互调用以相互调用.在程序设计中在程序设计中,常将一些常用的功能模块编写成函数常将一些常用的功能模块编写成函数,放在函放在函数库中供公共选用数库中供公共选用.main函数函数a函数函数e函数函数f函数函数g . . .函数函数b函数函数h函数函数I函数函数J . . .函数函数c函数函数K 函数函数L 函数函数M

3、. . .main( ) printstar( ); print_message( ); printstar( );printstar( ) printf(“ *n ”);print_message( ) printf(“How do you do!n”);说明说明: 1) 源程序文件可由一个或多个函数组成源程序文件可由一个或多个函数组成,其中其中主函数是不可缺省主函数是不可缺省的的.源程序文件是编译单位源程序文件是编译单位,函数函数不是编译单位不是编译单位; 2)一个一个C程序由一个或多个源程序文件组成程序由一个或多个源程序文件组成.较较大的大的C程序程序,常将一些函数和其他内容分别放在若常

4、将一些函数和其他内容分别放在若干源文件中干源文件中,再由若干源文件组成一个再由若干源文件组成一个C程序程序. 最简单的情况最简单的情况,一个一个C程序由一个源程序组成程序由一个源程序组成,这个源程序中只包含了一个函数这个源程序中只包含了一个函数-主函数主函数; 3)C程序的执行程序的执行从从main函数开始函数开始, 在在main函数函数中结束中结束. 4)所有函数在定义时是所有函数在定义时是相互独立相互独立的的,函数之间函数之间可以相互引用但不能嵌套定义可以相互引用但不能嵌套定义;函数的分类函数的分类:1)从从用户使用的角度用户使用的角度函数可分为函数可分为:标准函数标准函数,即即库函数库函

5、数.由系统提供由系统提供,用户不必定义用户不必定义,直接使用直接使用;用户自定义函数用户自定义函数.由用户根据需要由用户根据需要,自行编写自行编写,以解决专门需以解决专门需要要;2)从从函数的形式函数的形式分分,函数可分为函数可分为:无参数函数无参数函数.在调用无参函数时在调用无参函数时,主函数并不将数据传送给被主函数并不将数据传送给被调用函数调用函数,一般用来执行指定的一组操作一般用来执行指定的一组操作.无参函数可以无参函数可以带回也可以不带回函数值带回也可以不带回函数值,一般以后者居多一般以后者居多;有参函数有参函数.在调用函数时在调用函数时,在主函数和被调用函数之间有数据在主函数和被调用

6、函数之间有数据传递传递;8.2 函数定义的一般形式函数定义的一般形式1.无参函数的定义形式无参函数的定义形式类型标识符类型标识符 函数名函数名( ) 声明部分声明部分语句语句 其中,用其中,用“类型标识符类型标识符”指定函数值的类型指定函数值的类型,即函数带回来的,即函数带回来的类型类型.无参函数一般不需要带回函数值无参函数一般不需要带回函数值,因此可以不写类型标识符因此可以不写类型标识符;2.有参函数定义的一般形式有参函数定义的一般形式类型标识符类型标识符 函数名函数名 (形式参数表列形式参数表列 ) 声明部分声明部分语句语句例如例如:int max(int x,int y) int z;

7、z=xy?x:y; return(z);3.可以有可以有“空函数空函数”形式为形式为:类型说明符类型说明符 函数名函数名( ) 例如例如: dummy( ) ;调用此函数时调用此函数时,什么工作也不做什么工作也不做,没有任何实际作用没有任何实际作用.此中空函数的使用一般用于程序设计的模块设计阶段此中空函数的使用一般用于程序设计的模块设计阶段;4.对形参的声明方式对形参的声明方式 C语言传统声明方式为语言传统声明方式为:对形参类型的声明放在函数定对形参类型的声明放在函数定义的第义的第2行行,在函数括号外单独指定在函数括号外单独指定. 传统声明方式传统声明方式: int max(x, y) int

8、 x,y; int z; z=xy?x:y; return(z); 现代声明方式现代声明方式:int max(int x,int y) int z; z=xy?x:y; return(z); 8.3 函数参数和函数的值函数参数和函数的值8.3.1 形式参数和实际参数形式参数和实际参数1、形式参数(形参):、形式参数(形参):函数定义时设定的参数。函数定义时设定的参数。 下例中,函数头下例中,函数头int max(int x,int y)中中x,y就是形参,就是形参,它们的类型都是整型。它们的类型都是整型。 2、实际参数(实参):、实际参数(实参):调用函数时所使用的实际的参数。调用函数时所使用

9、的实际的参数。 下 例 中 , 主 函 数 中 调 用下 例 中 , 主 函 数 中 调 用 m a x 函 数 的 语 句 是 :函 数 的 语 句 是 :nmax=max(a,b); 其中其中a,b就是实参,它们的类型都是整就是实参,它们的类型都是整型。型。例例8.2 调用函数时的数据传递调用函数时的数据传递main ( ) int a,b,c; scanf(“%d,%d”,&a,&b); c=max(a,b); printf(“Max is %d”,c)int max( int x, int y) int z; z=xy?x:y; return(z);运行情况如下运行情况

10、如下:7,8Max is 8实参a,b形参x,y c=max(a, b); - 实参实参:在运行时 把函数的 max(int x,int y) 把值传给函数. 结果赋给 函数名 returu (z); 形参形参:通知系统 要预留内存位置. 关于形参和实参的说明关于形参和实参的说明 (1) 只用在只用在发生函数调用时发生函数调用时,形参才被分配内存单元形参才被分配内存单元.在调在调用结束后用结束后,形参所占的内存单元也被释放形参所占的内存单元也被释放; (2)实参可以是常量实参可以是常量,变量或表达式变量或表达式,如如:max(3,a+b) 但要求它们有但要求它们有确定的值确定的值.在调用时将实

11、参的值赋给形参在调用时将实参的值赋给形参. (3)在被定义的函数中在被定义的函数中,必须指定形参的类型必须指定形参的类型. (4)实参和形参的实参和形参的类型应相同或赋值兼容类型应相同或赋值兼容,否则会出错否则会出错; (5)C语言规定语言规定,实参对形参变量的数据传递是实参对形参变量的数据传递是“值传递值传递”,即即单向传递单向传递,只能由实参传给形参只能由实参传给形参,而不能由形参传回来而不能由形参传回来.8.3.2 函数的返回值函数的返回值 通常通常,希望通过函数调用使主调函数得到一个确定的希望通过函数调用使主调函数得到一个确定的值值,这就是函数的返回值这就是函数的返回值.例如例例如例8

12、.2.说明说明: (1)函数的返回值是通过函数中的函数的返回值是通过函数中的return语句语句获得的获得的. (2)一个函数中可以有一个函数中可以有一个以上一个以上的的return语句语句; (3)return语句后的括弧可以不要语句后的括弧可以不要;例如例如:return z;和和return(z);是等效的是等效的. (4)函数返回值的类型应当在函数返回值的类型应当在定义函数值时指定定义函数值时指定;在定义在定义函数时对函数值说明的类型一般应该和函数时对函数值说明的类型一般应该和return语句中的表语句中的表达式类型一致达式类型一致,如果不一致如果不一致,则则以函数类型为准以函数类型为

13、准. (5)如果被调用函数中没有如果被调用函数中没有return语句语句,并不带回一个确并不带回一个确定的定的,用户所希望的值用户所希望的值,带回的是一个不确定的值带回的是一个不确定的值,C语言允语言允许用户把该值赋给变量许用户把该值赋给变量.(6)为了明确表示为了明确表示“不带回值不带回值”,可以用可以用“void”定义无类型定义无类型.例如例如:例例8.1中的定义可以改为中的定义可以改为:void printstar( ) void print_message( ) 这样这样,系统就保证不使函数带回任何值系统就保证不使函数带回任何值,即禁止在调用函即禁止在调用函数中使用被调用函数的返回值数

14、中使用被调用函数的返回值.例如例如:a=printfstar( );b=print_message( );是不合法的是不合法的.8.4 函数的调用函数的调用8.4.1 函数调用的一般形式函数调用的一般形式函数调用的一般形式为函数调用的一般形式为:函数名函数名 ( 实参表列实参表列 )说明说明: (1)如果是调用无参函数如果是调用无参函数,则则“实参表列实参表列”可以没有可以没有,但但括括弧不能省略弧不能省略; (2)如果实参表列多个实参如果实参表列多个实参,则各参数间用则各参数间用逗号隔开逗号隔开,实参实参与形参的与形参的个数应相等个数应相等,类型应一致类型应一致;在在Turbo C中中,对实

15、参求值对实参求值的顺序是按自右至左顺序求值的的顺序是按自右至左顺序求值的. 8.4.2 函数调用的方式函数调用的方式 按函数在程序中出现的位置来分按函数在程序中出现的位置来分,可以有以下三种函数可以有以下三种函数调用方式调用方式:1.函数语句函数语句把函数调用作为一个语句把函数调用作为一个语句.如例如例8.1中的中的printstar( );这种不要求函数带回值这种不要求函数带回值,只要求函数只要求函数完成一定的操作完成一定的操作;2.函数表达式函数表达式函数出现在一个表达式中函数出现在一个表达式中,这种表达式称为函数表达式这种表达式称为函数表达式,这是要求函数这是要求函数带回一个确定的值带回

16、一个确定的值参加表达式的运算参加表达式的运算.例例如如:c=2*max(a,b);3.函数参数函数参数函数调用函数调用作为一个函数的实参作为一个函数的实参.这种调用的实质也是函这种调用的实质也是函数表达式调用的一种数表达式调用的一种.例如例如:m=max(a,max(b,c);其中其中max(b,c)是一次函数调用,它的值作为是一次函数调用,它的值作为max另一次另一次调用的实参。调用的实参。8.4.3 对被调用函数的声明和函数原型对被调用函数的声明和函数原型在一个函数中调用另一个函数需要具备的条件:在一个函数中调用另一个函数需要具备的条件:(1)首先被调用的函数必须是已经存在的函数(首先被调

17、用的函数必须是已经存在的函数(库函数库函数或或用用户自己定义的函数户自己定义的函数););(2)如果使用库函数,一般还应该在本文件开头用如果使用库函数,一般还应该在本文件开头用#include命令将调用有关库函数时所用到的信息命令将调用有关库函数时所用到的信息“包含包含”到本文件中到本文件中来。例如:来。例如:#include 其中其中“stdio.h”是一个是一个 “头文件头文件”,在,在stdio.h文件中放了文件中放了输入输出库函数输入输出库函数所用到的一些宏定义。如果不包含所用到的一些宏定义。如果不包含“stdio.h”文件中的信息,就无法使用输入输出库中的函数。同样,使文件中的信息,

18、就无法使用输入输出库中的函数。同样,使用数学库中的函数,应该用:用数学库中的函数,应该用:#include (3)如果使用用户自己定义的函数,如果使用用户自己定义的函数,函数调用与函数定义函数调用与函数定义前前,一般还应该在主调函数中对被调用函数作,一般还应该在主调函数中对被调用函数作声明声明。例例8.5 main( ) float add (float x, float y); float a,b,c; scanf(“%f,%f”,&a,&b); c=add(a,b); printf(“sum is %f”,c);float add(float x, float y) flo

19、at z; z=x+y; return(z); 对被调用函数的声明。从此行开始为对被调用函数的定义。包括函数首部、函数体等。 函数声明也称为函数声明也称为函数原形,函数原形,其作用主要是在程序的其作用主要是在程序的编译阶段对调用函数的合法性进行全面检查。编译阶段对调用函数的合法性进行全面检查。 在函数声明中也可以不写形参名,而只写形参的类型。在函数声明中也可以不写形参名,而只写形参的类型。如上例中可以这样进行函数声明:如上例中可以这样进行函数声明: float add(float, float);函数原型的一般形式为:函数原型的一般形式为: (1)函数类型函数类型 函数名(参数类型函数名(参数

20、类型1,参数类型参数类型2) (2)函数类型函数类型 函数名(参数类型函数名(参数类型1 参数名参数名1,参数类型参数类型2 参数名参数名2) 编译系统不检查参数名编译系统不检查参数名。因此上例程序中函数声明。因此上例程序中函数声明可以写成:可以写成:float add(float a,float b); 应当保证应当保证函数原型与函数首部写法上的一致函数原型与函数首部写法上的一致,即函,即函数类型、函数名,参数个数,参数类型和参数顺序必须数类型、函数名,参数个数,参数类型和参数顺序必须相同。函数调用时函数名、实参个数应与函数原型一致。相同。函数调用时函数名、实参个数应与函数原型一致。实参类型

21、必须与函数原型中的参数类型赋值兼容。实参类型必须与函数原型中的参数类型赋值兼容。 如果如果被调用函数的定义出现在主调函数之前被调用函数的定义出现在主调函数之前,可以不必加以声明可以不必加以声明。8.5函数的嵌套调用函数的嵌套调用 C语言的函数定义都是互相平行、独立的。语言的函数定义都是互相平行、独立的。C语句不语句不能嵌套定义函数,但能嵌套定义函数,但可以嵌套调用函数可以嵌套调用函数,也就是说,在,也就是说,在调用一个函数的过程中,又调用另一个函数。调用一个函数的过程中,又调用另一个函数。 下图表示的是两层嵌套(连下图表示的是两层嵌套(连main函数共函数共3层函数),层函数),其执行过程见图

22、。其执行过程见图。开始开始调用调用a函数函数结束结束开始开始调用调用b函数函数开始开始main函数a函数b函数 8.6函数的递归调用函数的递归调用 在调用一个函数的过程中又出现直接或间接地调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。该函数本身,称为函数的递归调用。C语言的特点之一就语言的特点之一就在于允许函数的递归调用。例如:在于允许函数的递归调用。例如:int f(int x) int y,z; z=f(y); return(2*z);在调用函数在调用函数f 的过程中,又要调用的过程中,又要调用f 函数,这是直接调函数,这是直接调用本函数用本函数.见图。见

23、图。 开始开始调用调用f函数函数f 函数在调用在调用 f1 函数过程中要调用函数过程中要调用 f2 函数,而在调用函数,而在调用f2函数过程中又要调用函数过程中又要调用 f1 函数。函数。从图上可以看到,这两种递归调用都是从图上可以看到,这两种递归调用都是无终止无终止的自的自身调用。可以身调用。可以用用if语句来控语句来控 制制,只有在某一条件成立,只有在某一条件成立时才继续执行递归调用,否则就不再继续。时才继续执行递归调用,否则就不再继续。开始开始调用调用f2函数函数开始开始调用调用f1函数函数f1 函数f2 函数例例8.7 有有5个人坐在一起,问第个人坐在一起,问第5个人多少岁?他说比第个

24、人多少岁?他说比第4个人大个人大2岁。问第岁。问第4个人岁数,他说比第个人岁数,他说比第3个人大个人大2岁。问岁。问第第3个人,又说比第个人,又说比第2个人大个人大2岁。问第岁。问第2个人,说比个人,说比 第第1个个人大人大 2岁。最后问第岁。最后问第1个人,他说是个人,他说是10岁。请问第岁。请问第5个人多个人多大。大。 显然,这是一个递归问题。每一个年龄都比其前显然,这是一个递归问题。每一个年龄都比其前1个人的个人的年龄大年龄大2岁即岁即 age(5)=age(4)+2 age(4)=age(3)+2 age(3)=age(2)+2 age(2)=age(1)+2 age(1)=10因此,

25、除了第一个人,其余的人的年龄都需用递归法求因此,除了第一个人,其余的人的年龄都需用递归法求出。出。执行过程如下:执行过程如下:age函数函数n=5mainage(5)输出输出age(5)c=age(4)+2age函数函数n=4c=age(3)+2age函数函数n=3c=age(2)+2age函数函数n=2c=age(1)+2age函数函数n=1c=10age(2)=12age(3)=14age(4)=16age(5)=18int age(int n) /*递归函数递归函数*/ int c;if(n=1) c=10;else c=age(n-1)+2;return(c);main ( ) /*主

26、函数主函数*/ printf(“%d”,age(5)递归结束条件递归结束条件递归公式递归公式递归函数编写总结:递归函数编写总结:1) 采用采用if语句格式;语句格式;2)两要素:两要素: 递归结束条件递归结束条件 递归公式递归公式3)一般形式一般形式 if(递归结束条件递归结束条件) else 递归公式递归公式 4)递归公式中包含递归函数递归公式中包含递归函数名,但参数不同名,但参数不同5)if语言前的语句在递归进语言前的语句在递归进入时执行,入时执行,if语句后的语句语句后的语句在递归返回时执行在递归返回时执行例例8.8用递归的方法求用递归的方法求n!递推法:递推法:从从1开始,乘开始,乘2

27、,乘,乘3,一直乘到一直乘到n。其基本。其基本 原理是从一个已知事实推出下一个事实。原理是从一个已知事实推出下一个事实。递归法:递归法:当当n=1或或0时,时,n!=1;当;当n1时,时,n!=n*(n-1)!。float fac(int n) float f; if(n0) printf(“n0,dataerror!”); else if(n=0|n=1) f=1; else f=fac(n-1)*n; return(f); main ( ) int n; float y; printf(“input a integer number:”; scanf(“%d”,&n); y=fac

28、(n); printf(“%d!=%15.0f”,n,y);8.7 数组作为函数参数数组作为函数参数1.数组元素作为函数实参数组元素作为函数实参数组元素数组元素作函数实参:作函数实参: “值传递值传递”方式,即单向传方式,即单向传递递。例例8.10 有两个数组有两个数组a,b,各有,各有10个元素,将它们对应地个元素,将它们对应地逐个相比。如果逐个相比。如果a数组中的元素大于数组中的元素大于b数组中的相应元素数组中的相应元素的数目多于的数目多于b数组中元素中大于数组中元素中大于a数组中相应元素的数目,数组中相应元素的数目,则认为则认为a数组大于数组大于b数组,并分别统计出两个数组相应元数组,并

29、分别统计出两个数组相应元素大于、等于、小于的次数。素大于、等于、小于的次数。分析分析:1)对于两个数组中的元素比较结果,我们可以用对于两个数组中的元素比较结果,我们可以用3个变量来记录;个变量来记录;2)为了保持程序的简洁,用一个函数来进行数组为了保持程序的简洁,用一个函数来进行数组元素的比较,比较结果用该函数的返回值表示。元素的比较,比较结果用该函数的返回值表示。main ( )int large(int x,int y);int a10,b10,i,n=0,m=0,k=0;printf(“enter array a:n”); for(i=0;i10;i+)scanf(“%d”,&a

30、i);printf(“n”);printf(“enter array b:n”); for(i=0;i10;i+)scanf(“%d”,&bi);printf(“n”);for(i=0;ibi%d imesn ai=bi%d imesn aik)printf(“array a is larger than array bn”);if(ny) flag=1;else if (xy) flag=-1;else flag=0;return(flag);输入数组输入数组a和数组和数组b的数据的数据调用调用large函数进行函数进行数组元素比较数组元素比较large函数,返回比函数,返回比较结果

31、较结果2.数组名可作函数参数数组名可作函数参数数组名可作函数参数,此时数组名可作函数参数,此时实参与形参都应用数组名实参与形参都应用数组名。例例8.11 有一个一维数组有一个一维数组score,内放,内放10个学生成绩,求平均成绩。个学生成绩,求平均成绩。float average(float array10) int i;float aver,sum=array0;for(i=1;i10;i+) sum=sum+arrayi;aver=sum/10;return(aver);main( )float score10,aver;int i;printf(“input 10 score:n”);

32、for(i=0;i10;i+) scanf(“%f”,&scorei);printf(“n”);aver=average(score);printf(“average score is %5.2f”,aver);说明:说明:1)用数组名作函数参数,用数组名作函数参数,应该在主调函数应该在主调函数和被调用函数分别定义数组和被调用函数分别定义数组,不能只在一方定义;,不能只在一方定义;2)实参数组与形参数组实参数组与形参数组类型应一致类型应一致,否则,否则出错;出错;3)实参数组与形参数组实参数组与形参数组大小可以一致也可大小可以一致也可以不一致以不一致,C编译对形参数组大小不作检查,编译

33、对形参数组大小不作检查,只只是将实参数组的首地址传给形参数组是将实参数组的首地址传给形参数组; 4)形参数组也可以不指定大小,在定义数形参数组也可以不指定大小,在定义数组时在数组名后面跟一个空的方括弧,为了在被组时在数组名后面跟一个空的方括弧,为了在被调用函数中处理数组元素的需要,调用函数中处理数组元素的需要,可以另设一个可以另设一个参数,传递数组元素的个数参数,传递数组元素的个数。参见。参见P166例例8.12 5)数组名作函数实参,不是值传递而是数组名作函数实参,不是值传递而是地地址传递址传递,实参和形参数组将共占用同一段内存,实参和形参数组将共占用同一段内存单元,如果形参数组中各元素发生

34、变化会使实参单元,如果形参数组中各元素发生变化会使实参数组元素的值同时发生变化数组元素的值同时发生变化。例例8.13 用选择法对数组中用选择法对数组中10个整数按由小到达排序。个整数按由小到达排序。选择法介绍:选择法介绍:1)将)将10个数进行相互比较,找出最小数,将之与个数进行相互比较,找出最小数,将之与a0对换;对换;2)再将)再将a1到到a9的数进行比较,找出最小数,将之与的数进行比较,找出最小数,将之与a1对换;对换;3),共比较,共比较9轮,最后得到排序后的结果;轮,最后得到排序后的结果;以以5个数为例说明:个数为例说明:a0 a1 a2 a3 a4 3 6 1 9 4 未排序时的情

35、况未排序时的情况 1 6 3 9 4 将将5个数中最小数个数中最小数1与与a0对换对换 1 3 6 9 4 将余下的将余下的4个数中最小数个数中最小数3与与a1对换对换 1 3 4 9 6 将余下的将余下的3个数中最小数个数中最小数4与与a2对换对换 1 3 4 6 9 将余下的将余下的2个数中最小数个数中最小数6与与a3对换对换至此完成排序至此完成排序void sort(int array ,int n) int i,j,k,t;for(i=0;in-1;i+) k=i; for(j=i+1;jn;j+) if(arrayjarrayk) k=j; t=arrayk;arrayk=array

36、i;arrayi=t; main( ) int a10,i; printf“enter the arrayn”); for(i=0;i10;i+) scanf(“%d”,&ai); sort(a,10); printf(the sorted array:n); for(i=0;ib?a:b;return(c);main( ) int a=8;printf(“%d”,max(a,b);结果为:结果为:8a,b为全局变量a,b为局部变量形参a,b作用范围局部变量a作用范围全局变量b作用范围8.9 变量的存储类别变量的存储类别8.9.1 动态存储方式与静态存储方式动态存储方式与静态存储方式按

37、按作用域作用域来分:来分: 全局变量和局部变量。全局变量和局部变量。按按变量值生存期变量值生存期来分:静态存储方式、动态存储方式。来分:静态存储方式、动态存储方式。 静态存储方式:静态存储方式:在程序运行期间分配固定的存储空间在程序运行期间分配固定的存储空间的方式的方式 动态存储方式:动态存储方式:在程序运行期间根据需要进行动态的在程序运行期间根据需要进行动态的分配存储空间的方式分配存储空间的方式。 内存中的供用户使用的存储空间的情况。这个存储内存中的供用户使用的存储空间的情况。这个存储空用户区间。分为三部分,见图。空用户区间。分为三部分,见图。程序区程序区静态存储区静态存储区动态存储区动态存

38、储区用户区用户区数据存放区数据存放区 全局变量全局变量全部全部存放在存放在静态存储区:静态存储区: 非动态地进行分配和释放,在非动态地进行分配和释放,在程序开始程序开始执行时给全局变执行时给全局变量分配存储区,量分配存储区,程序执行完毕程序执行完毕就释放。在程序执行过程中它们就释放。在程序执行过程中它们占据固定的存储单元。占据固定的存储单元。 动态存储区动态存储区中存放以下数据:中存放以下数据: 1.函数形式参数函数形式参数。2.自动变量自动变量(未加(未加static声明的局部变声明的局部变量)。量)。 3.函数调用别的现场保函数调用别的现场保 护和返回地址护和返回地址等。等。 动态地进行分

39、配和释放。在动态地进行分配和释放。在函数调用函数调用开始时分配动态存开始时分配动态存储空间,储空间,函数结束函数结束时释放这些空间。时释放这些空间。 在在C语言中每一个语言中每一个变量变量和和函数函数有有两个属性两个属性:数据类型数据类型和和数数据存储类别据存储类别。数据类型包括整型、字符型等。存储类别措的是。数据类型包括整型、字符型等。存储类别措的是数据在内存中存储的方法,存储方法分数据在内存中存储的方法,存储方法分 为两大类:为两大类:静态存储静态存储类类和和动态存储类动态存储类。具体包含四种:。具体包含四种:自动的(自动的(auto),静态的静态的(static)寄存器的(寄存器的(re

40、gister),外部的(外部的(extern),根据,根据变量的存储类别,可以知道变量的作用域和生存期。变量的存储类别,可以知道变量的作用域和生存期。8.9.2 auto变量变量 函数中的局部变量,如不专门声明为函数中的局部变量,如不专门声明为static存储类别,存储类别,都是动态地分配存储空间的,都是动态地分配存储空间的, 数据存储在动态存储区中数据存储在动态存储区中在函数调用结束时就自动释放这些存储空间,因此在函数调用结束时就自动释放这些存储空间,因此这类这类局部变量称为局部变量称为自动变量自动变量。自动变量用关键字。自动变量用关键字auto作存储作存储类别的声明。例如:类别的声明。例如

41、: int f (int a) /*定义定义f函数,函数,a为形参为形参*/ auto int b,c=3 /*定义定义b,c为自动变量为自动变量*/ 实际上,关键字实际上,关键字“auto”可以省略,可以省略,auto不写则隐含不写则隐含确定为确定为“自动存储类别自动存储类别” 。程序中大多数变量属于自动。程序中大多数变量属于自动变量。我们前面介绍的函数中定义的变量都没有声明为变量。我们前面介绍的函数中定义的变量都没有声明为auto,其实都隐含指定为自动变量。,其实都隐含指定为自动变量。8.9.3 用用static声明局部变量声明局部变量 有时希望函数中的局部变量的值在有时希望函数中的局部变

42、量的值在函数调用结束后函数调用结束后不消失而保留原值不消失而保留原值,即其占用的存储单元不释放,在下,即其占用的存储单元不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。这时就应该指定该局部变量为用结束时的值。这时就应该指定该局部变量为”静态局静态局部变量部变量”,用关键字,用关键字static。进行声明。进行声明。例例8.17 f (int a) auto int b=0; static int c=3; b=b+1; c=c+1; return(a+b+c);main ( ) int a=2,i; for(i=0;i3

43、;i+) printf(“%d”,f(a);运行结果为:7 8 9说明:说明:1)静态局部变量属于静态存储类别,在静态存储区内静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在程序整个运行期间都不释放;分配存储单元,在程序整个运行期间都不释放;2)对对静态局部变量静态局部变量是在是在编译时赋初值编译时赋初值的,在程序运行的,在程序运行时它已有初值,以后每次调用函数时不再重新赋初值时它已有初值,以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而只是保留上次函数调用结束时的值。 自动变量赋初值自动变量赋初值,不是在编译时进行,而是,不是在编译时进行,而是在函在函数调用时

44、进行数调用时进行,每调用一次函数重新给一次初值,相,每调用一次函数重新给一次初值,相当于执行一次赋值语句;当于执行一次赋值语句;3)如在定义局部变量时不赋初值的话,对如在定义局部变量时不赋初值的话,对静态局部变静态局部变量量,编译时自动赋初值,编译时自动赋初值0(对数值型变量对数值型变量)或或空字符空字符(对字对字符变量符变量)。而对。而对自动变量自动变量来说,如果不赋初值则它的值来说,如果不赋初值则它的值是一个是一个不确定的值不确定的值。4)虽然静态局部变量在函数调用结束后仍然存在,但其虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。一般在以下情况下使用局部静他函数是不能

45、引用它的。一般在以下情况下使用局部静态变量:态变量: (1)需要保留函数上一次调用结束时的值。需要保留函数上一次调用结束时的值。 (2)如果初始化后,变量只被引用而不改变其值,如果初始化后,变量只被引用而不改变其值,则这时用静态局部变量比较方便,以免每次调用时重新则这时用静态局部变量比较方便,以免每次调用时重新赋值。赋值。 但是应该看到,用静态存储要多占内存,而且降低但是应该看到,用静态存储要多占内存,而且降低了程序的可读性,当调用次数多时往往弄不清静态局部了程序的可读性,当调用次数多时往往弄不清静态局部变量的当前值是什么。因此,如不必要,不要多用静态变量的当前值是什么。因此,如不必要,不要多

46、用静态局部变量。局部变量。8.9.4 register变量变量 一般情况下,一般情况下,变量变量(包括静态存储方式和动态存储方式)的值(包括静态存储方式和动态存储方式)的值是是存放在内存中的存放在内存中的,当程,当程 序中用到哪一个变量的值时,由控制器发序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。经过运算器进行运算,出指令将内存中该变量的值送到运算器中。经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。如果需要存数,再从运算器将数据送到内存存放。 如果有一些变量使用频繁,则从内存存取数据将花费较多时间。如果有一些变量使用频繁,则从内存存取数据将花费较

47、多时间。为了提高执行效率,为了提高执行效率,C语言允许将局部变量的值放在语言允许将局部变量的值放在CPU中的寄存中的寄存器中器中。由于对寄存器的存取速度远高于对内存的存取速度,因此这。由于对寄存器的存取速度远高于对内存的存取速度,因此这样就可以提高执行效率。这种变量叫做样就可以提高执行效率。这种变量叫做”寄存器变量寄存器变量”,用关键字,用关键字register作声明作声明,内存CPU总线运算器 控制器寄存器例例8.19 使用寄存器变量使用寄存器变量int fac(int n)register int i,f=1;for(i=1;i=n;i+) f=f*i;return(f);main( )i

48、nt i;for(i=1;i=5;i+) printf(“%d!=%dn”,i,fac(i);说明:说明:1))只有只有局部自动变量局部自动变量和和形式参数形式参数可以作为寄存器变量,可以作为寄存器变量,其他(如全局变量)不行。其他(如全局变量)不行。2)一个计算机系统中的寄存器一个计算机系统中的寄存器数目是有限数目是有限的,不能定义的,不能定义任意多个寄存器变量。任意多个寄存器变量。3)不同的系统对寄存器变量的处理方法是不同的,有的不同的系统对寄存器变量的处理方法是不同的,有的系统对寄存器不良当自动变量处理,分配内存单元,有系统对寄存器不良当自动变量处理,分配内存单元,有的系统只允许将的系统

49、只允许将int,char和指针型变量定义为寄存器变和指针型变量定义为寄存器变量;量;当今的优化编译系统能够识别使用频繁的变量,从当今的优化编译系统能够识别使用频繁的变量,从而自动地将这些变量放在寄存器中,而不需要程序设计而自动地将这些变量放在寄存器中,而不需要程序设计者指定;者指定;4)局部静态变量局部静态变量不能定义为不能定义为寄存器变量寄存器变量。 8.9.5 用用extern声明外部变量声明外部变量 外部变量(即全局变量)外部变量(即全局变量)是在函数的外部定义的,是在函数的外部定义的,它的作用域为从它的作用域为从变量的定义处开始,到本程序文件的未变量的定义处开始,到本程序文件的未尾。在

50、此作用域内,全局变量可以为程序中各个函数所尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时,将外部变量分配在静态存储区。引用。编译时,将外部变量分配在静态存储区。 有时需要用有时需要用extern来声明外部变量,以来声明外部变量,以扩展外部变量扩展外部变量的作用域的作用域.1.在一个文件内声明外部变量在一个文件内声明外部变量 如果外部变量不在文件的开头定义,其如果外部变量不在文件的开头定义,其有效的作用范有效的作用范围只限于定义处到文件终了围只限于定义处到文件终了。如果在定义点之前的函数。如果在定义点之前的函数恩引用该外部变量,则应该在引用之前用关键字恩引用该外部变量,则应该在引用

51、之前用关键字exturn对该变量作对该变量作“外部变量声明外部变量声明”。表示该变量是一个已经。表示该变量是一个已经定义的外部变量。有了此声明,就可以定义的外部变量。有了此声明,就可以从从“声明声明”处开处开始,合法使用该外部变量始,合法使用该外部变量。2.在多文件的程序中声明外部变量在多文件的程序中声明外部变量 一个一个C程序可以由一个或多个源程序文件组成。如程序可以由一个或多个源程序文件组成。如果程序只由一个源文件组成,使用外部变量的方法如前果程序只由一个源文件组成,使用外部变量的方法如前面介绍。面介绍。 如果一个程序包含两个文件,在两个文件中都要用如果一个程序包含两个文件,在两个文件中都

52、要用到同一个外部变量,不能分别在两个文件中对该变量各到同一个外部变量,不能分别在两个文件中对该变量各自定义,否则进行程序的连接时会出现自定义,否则进行程序的连接时会出现“重复定义重复定义”的的错误。正确的做法是:错误。正确的做法是:在任一个文件中定义该变量,而在任一个文件中定义该变量,而在另一文件中用在另一文件中用extern对该变量作对该变量作“外部变量声明外部变量声明” 在在编译和连接时系统会由此知道此变量是一个已在别处定编译和连接时系统会由此知道此变量是一个已在别处定义的外部变量,井将在另一文件中定义的外部变量的作义的外部变量,井将在另一文件中定义的外部变量的作用域扩展到本文件用域扩展到

53、本文件,在本文件中可以合法地引用外部变,在本文件中可以合法地引用外部变量。量。例例8.21 给定给定b的值,输入的值,输入a和和m,求,求a*b和和am的值。的值。文件文件file1.c中的内容:中的内容:int A; /*定义外部变量*/main( ) input power(int);/*对调用函数作声明*/int b=3,c,d,m;printf(“enter the number a and its power m:n”);scanf(“%d,%d”,&a,&m);c=A*b;printf(“%d*%d=%dn”,A,b,c);d=power(m);printf(“%d

54、*%d=%d”,A,m,d);文件文件file2.c中的内容:中的内容:extern A; /*声明A为一个已定义的外部变量*/power(int n); int i,y=1;for(i=1;i=n;i+)y=y*A;return(y);说明:说明: 1)假如程序有假如程序有5个源文件,在一个文件中定义外部个源文件,在一个文件中定义外部整型变量整型变量A,其他,其他4个文件都可以引用个文件都可以引用A,但必须在每一,但必须在每一个文件中都加上一个个文件中都加上一个extern A声明。在各文件经过编译声明。在各文件经过编译后,将备目标文件联接成一个可执行的目标文件;后,将备目标文件联接成一个可

55、执行的目标文件; 2)在编译时遇到在编译时遇到extern时,先在本文件中找外部变时,先在本文件中找外部变量的定义,如果找到,就在本文件中扩展作用域,如果量的定义,如果找到,就在本文件中扩展作用域,如果找不到,就在连接时从其他文件中找外部变量的定义,找不到,就在连接时从其他文件中找外部变量的定义,如果找到,就将作用域扩展到本文件。如果找不到,按如果找到,就将作用域扩展到本文件。如果找不到,按出错处理。出错处理。8.9.6 用用static声明外部变量声明外部变量 有时在程序设计中希望有时在程序设计中希望某些外部变量只限于被本文某些外部变量只限于被本文件引用,而不能被其他文件引用件引用,而不能被

56、其他文件引用。这时可以在定义外部。这时可以在定义外部变量时加一个变量时加一个static声明。声明。 这种加上这种加上static声明,只能用于本文件的外部变量声明,只能用于本文件的外部变量(全局变量)称为静态外部变量。这为程序的模块化、(全局变量)称为静态外部变量。这为程序的模块化、通用性提供方便。如果已知道其他文件不引用本文件的通用性提供方便。如果已知道其他文件不引用本文件的外部变量,可以对本文件中的外部变量都加上外部变量,可以对本文件中的外部变量都加上static,成,成为静态外部变量,以免彼其他文件误用。为静态外部变量,以免彼其他文件误用。 需要指出对外部变量加上需要指出对外部变量加上

57、static声明,并不意味着这声明,并不意味着这时才是静态存储(存放在静态存储区中),而不是时才是静态存储(存放在静态存储区中),而不是static的是动态存储(存放在动态存储区)。两种形式的外部的是动态存储(存放在动态存储区)。两种形式的外部变量都是静态存储方式,只是作用范围不同而已,都是变量都是静态存储方式,只是作用范围不同而已,都是在编晖时分配内存的。在编晖时分配内存的。8.10 内部函数和外部函数内部函数和外部函数 函数本质上是全局的,因为一个函数要被另外函数函数本质上是全局的,因为一个函数要被另外函数调用。但是,也可以指定函数不能被其他文件调用。根调用。但是,也可以指定函数不能被其他文件调用。根据据函数能否被其他源文件调用函数能否被其他源文件调用,将函数区分为,将函数区分为内部函数内部函数和和外部函数外部函数。8.10.1 内部函数内部函数 如果一个函数只能被本文件中其他函数所调用,它如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类称为内部函数。在定义内部函数时,在函数名和函数类型的前面加型的前面

温馨提示

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

评论

0/150

提交评论