c语言程序设计函数调用课件_第1页
c语言程序设计函数调用课件_第2页
c语言程序设计函数调用课件_第3页
c语言程序设计函数调用课件_第4页
c语言程序设计函数调用课件_第5页
已阅读5页,还剩95页未读 继续免费阅读

下载本文档

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

文档简介

第7章函数与变量7.1函数概述7.2函数参数和返回值7.3函数的调用7.4数组的作为函数参数7.5变量的定义位置和作用域7.6动态存储方式与静态存储方式7.7函数的存储分类7.8程序设计举例7.9程序设计题目第7章函数与变量7.1函数概述1

C语言函数分为两种:

标准函数和用户自定义的函数。

标准函数是系统提供的已设计好的函数,可以直接调用,用户自己定义的函数是用户自己编写的用来解决具体问题的函数.(1)C程序的执行从main函数开始,调用其他函数后流程回到main函数。(2)所有函数都是平行的,即在定义函数时,是互相独立的,一个函数并不从属于另一函数,即函数不能嵌套定义,但可以互相调用,但不能调用main函数。(3)从用户使用的角度看,函数分两种:①标准函数,即库函数。这是由系统提供的,用户不用自己定义。②用户自己定义的函数,以解决用户的专门问题。7.1函数概述7.1函数概述2(4)从函数的形式看,函数分两类:

①无参函数:主调函数并不将数据传送给被调用函数。

②有参函数:在调用函数时,在主调函数和被调用函数之间有参数传递,也就是说,主调函数可以将数据传给被调用函数使用,被调用函数中的数据也可以带回来供主调函数使用。7.1.2函数的定义

1.无参函数的定义

类型标识符函数名(){说明部分或语句}

说明:

用“类型标识符”指定函数值的类型,即函数带回来的值的类型。C语言默认返回值的类型是整型。2.有参函数的定义

类型标识符函数名(形式参数说明){说明部分语句}(4)从函数的形式看,函数分两类:类型标识符函数名3

类型标识符函数名(形式参数表列)形式参数说明{说明部分语句}例如:intmax(x,y)

intx,y;/*形式参数说明*/{intz;/*函数体中的说明部分*/z=x>y?x:y;return(z);/*z为函数的返回值*/

}这是一个求x和y二者中大者的函数,x和y为形式参数,由主调函数的实际参数把参数值传递给被调用函数中的形式参数x和y。类型标识符函数名(形式参数表列)47.2.1函数参数在调用函数时,大多数情况下,主调函数和被调函数之间有数据传递关系。在定义函数时,函数名后面括号中变量名称为“形式参数”,简称“形参”。在调用函数时,函数名后面括号中的表达式称为“实际参数”,简称“实参”。(1)在定义函数中指定的形参变量,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时函数max中的形参才被分配内存单元。调用结束后,形参所占用的内存单元也同时被释放。(2)实参可以是常量、变量或表达式,如:max(3,a+b);但要求它们有确定的值。在调用时将实参的值赋给形参变量。7.2函数参数和返回值7.2.1函数参数7.2函数参数和返回值5(3)在定义的函数中,必须指定形参的类型。(4)实参与形参的类型应一致。只有字符型与整型可以互相通用。(5)实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。7.2.2函数的返回值函数的返回值是由return语句传递的。

格式:return(表达式);或return表达式;

功能:用return语句从函数退出,返回到调用它的程序中.该语句有两重作用:(1)从函数中退出,返回到调用它的程序中。(2)向调用程序返回一个值。(3)在定义的函数中,必须指定形参的类型。6

7.3.1函数调用的一般形式格式:函数名(实参表列);

函数调用语句的执行过程:首先计算每个实参表达式的值,并把此值存入所对应的形参单元中,然后把执行流程转入函数体中,执行函数体中的语句,函数体执行完之后,将返回到调用此函数的程序中的下一条语句,继续去执行。当执行到函数体的右花括号或return语句时,表示函数体执行完成,这时将返回到主调程序中。

7.3函数的调用7.3函数的调用77.3.2对被调用函数的说明在一个函数中调用另一个被调函数,需要具备的条件:

(1)首先被调函数必须是已经存在的函数(库函数或用户自定义函数)。(2)如果使用库函数,一般还应在本文件开头用#include命令将调用有关库函数时所需用的信息包含到本文件中来。①输入输出库函数:#include<stdio.h>——使用系统定义的标准输入输出函数。②数学库函数:#include″math.h″———使用系统定义的标准数学运算函数。(3)如果使用用户自己定义的函数,而且该函数与调用它的函数(即主调函数)在同一个文件中,一般还应该在主调函数中对被调用函数的返回值的类型作说明。7.3.2对被调用函数的说明8这种类型说明的一般形式为:类型标识符被调函数函数名();

对被调用函数的说明,在以下几种情况下可以省略:

(1)如果函数的值(函数的返回值)是整型或字符型,可以不进行说明,系统对它们自动按整型说明。(2)如果被调用函数的定义出现在主调函数之前,可以不进行说明,因为编译系统已经先知道了已定义的函数类型,会自动处理。(3)如果在所有函数定义之前,对函数类型进行了说明,则在各个主调函数中不再进行说明。

7.3.4函数的嵌套调用C语言的函数定义都是互相平行、独立的,可以嵌套调用函数即在调用一个函数的过程中,又调用另一个函数.

这种类型说明的一般形式为:97.3.5函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数递归调用。例如:intf(x)intx;{inty,z;…z=f(y);…return(2*z);

}7.3.5函数的递归调用107.4.1数组元素做函数实参由于实参可以是表达式,数组元素又可以是表达式的组成部分,因此数组元素可以作为函数的实参,与变量作实参一样,是单向传递,即“值传送”。只能将数组元素的值传递给被调函数的形参,不能带回变化的值。这种方式适合部分数组元素传递和元素较小的数组传递。

7.4数组的作为函数参数7.4数组的作为函数参数11运行结果:形参值为:234实参值为:1237.4.2用数组名做函数的实参与形参例7.1有一个一维数组s,存放10个学生成绩,求平均成绩.

floataverage(a)/*数组名a作形参*/floata[10];{inti;floatv,sum=a[0];for(i=1;i<10;i++)sum=sum+a[i];v=sum/10;return(v);

}运行结果:形参值为:23412main(){floats[10],v;inti;printf(″input10s:\n″);for(i=0;i<10;i++)scanf(″%f″,&s[i]);printf(″\n″);v=average(s);/*数组名s作实参*/printf(″averageis%5.2f″,v);}运行结果:input10s:

100567898.576879967.57597≠averageis83.40main()13

说明:

(1)用数组名做函数参数,应该在主调函数和被调函数中分别定义数组。(2)实参数组与形参数组类型应一致。(3)实参数组和形参数组大小可以一致也可以不一致,C编译时对形参数组大小不作检查,只是将实参数组的首地址传给形参数组。若要求形参数组得到实参数组全部的元素值,则应当指定形参数组与实参数组大小一致。也可以不指定形参数组大小,在定义数组名后面跟一个空的方括弧,为了在被调函数中处理数组元素的需要,可以另设参数,说明传递数组元素的个数。例7.2floataverage(a,n)/*a为形参数组名,n用于说明数组元素个数*/floata[];说明:14intn;{

inti;floatv,sum=a[0];for(i=1;i<n;i++)sum=sum+a[i];v=sum.n;return(v);}main(){

floatscore1[5]={98.5,97,91.5,60,55};floatscore2[8]={67,88.5,79,45,90,81,77.5,99};intn;15printf(″theaverageofclass1is%6.2f\n″,average(score1,5));/*调用时5为形参数组a的元素个数*/printf(″theaverageofclass2is%6.2f\n″,average(score2,8));}运行结果:theaverageofclass1is80.40theaverageofclass2is78.37这么处理数组的大小就可以不同。(4)数组名做函数参数时,把实参数组的起始地址传递给形参数组,这样两个数组就共占同一段内存单元。如图7.1所示.假如a数组的起始地址为1000,则s数组的起始地址也为1000,显然,a和s同占一段内存单元。a[0]与s[0]同占一个单元…。printf(″theaverageo16这种传递方式叫“地址传递”。由此可以看到,形参数组中各元素的值发生变化,会使实参数组元素的值同时发生变化。这一点与变量函数参数的情况不同。

a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]2468101214161820s[0]s[1]s[2]s[3]s[4]s[5]s[6]s[7]s[8]s[9]C语言中变量的定义有三个基本位置:函数内部、函数参数中及所有函数外部。这些变量分别称为局部变量、形式参数变量、全局变量。定义位置不同,变量的作用域不同,即变量所起作用的范围不同。有关形式参数已经介绍过。图7.1用数组名做参数时,形参与实参的存储关系7.5变量的定义位置和作用域这种传递方式叫“地址传递”。由此可以看到,形参数组中177.5.1局部变量局部变量又称做内部变量,是在函数内部定义的变量。形参也是局部变量。其作用域是从定义的位置起,到函数体结束止。也就是说,只能在定义该变量的函数内使用它,在此函数以外不能使用。对于局部变量,它只在进入本函数时生成,在退出该函数时消失。因此在C语言中不同的函数内可以定义相同名字的变量,它们代表的对象不同,互不影响。例如:f1(){inta,b;a=5;b=10;…}局部变量a,b的作用域7.5.1局部变量局部变量a,b的作用域18f2(){inta,b;a=100;b=-200;…}f1()和f2()中的变量a、b均为局部变量,并且互不相关。其原因是每个a、b作为局部变量仅在被定义的函数内是可知的。在复合语句中也可以定义本段程序的局部变量。例如:局部变量a,b的作用域f2()局部变量a,b的作用域19f(x)intx;{inta;…if(x>0){

intb=40;…

…}b的作用域仅在其定义的分程序之内,即进入分程序时建立b变量,结束分程序时立即撤消。b的作用域x、a的作用域f(x)b的作用域x、a的作用域207.5.2全局变量全局变量又称做外部变量,是在函数外部定义的变量。其有效范围是从变量定义的位置开始到本源文件结束止。若在同一个源文件中,局部变量与全局变量同名,则在局部变量的作用范围内,全局变量被屏蔽,不起作用。在函数体外进行的函数说明也使该函数具有全局的性质。7.5.2全局变量21inta=1,b=5;floatf1(x)intx;

{intc,d;…

}charc1,c2;charf2(x,y)intx,y;{inti,j;…}main(){intm,n;…

}全局变量c1、c2作用域全局变量a、b作用域inta=1,b=5;全局变量c1、c2作用域全局22a、b、c1、c2都是全局变量,但它们的作用范围不同。在main函数和函数f2中可以使用全局变量a、b、c1、c2,但在函数f1中,只能使用全局变量a、b,而不能使用c1和c2。

说明:

(1)全局变量的作用:增加函数间数据联系的渠道。由于同一文件中的所有函数都能引用全局变量的值,因此,如果在一个函数中改变了全局变量的值,就能影响到其他函数,相当于各个函数间有直接的传递通道。由于函数的调用只能带回一个返回值,因此有时可以利用全局变量增加与函数联系的渠道,从函数得到一个以上的返回值。对于全局变量,如果在定义时不进行初始化,则系统将自动赋予其初值,对数值型赋0,对于字符型赋空′\0′。

a、b、c1、c2都是全局变量,但它们的作用范围不同23(2)使用全局变量会增加程序的内存开销,因为全局变量在程序的整个执行过程中都有效,即一直占用着内存单元,而不是像局部变量那样,在进入其所在函数时才开辟存储单元,退出函数时便将其释放。使用全局变量,还会降低函数通用性,而且会降低程序的清晰度。建议不要无限制地使用全局变量。(3)在定义全局变量时,最理想的定义位置是在源文件的开头处,这样,在整个文件中的所有函数均可使用该变量。如果将一全局变量定义在源程序文件的中间,则其前面的函数不能使用该变量。如果在定义点之前的函数想引用该外部变量,则应该在该函数中用关键字extern作“外部变量说明”。表示该变量在函数的外部定义,在函数内部可以使用它们。见例7.10.例7.3求两个数的最大值。

intmax(x,y)intx,y;(2)使用全局变量会增加程序的内存开销,因为全局变量在24{

intz;z=x>y?x:y;return(z);}main(){externinta,b;/*外部变量说明*/printf(″max=%d\n″,max(a,b));

}inta=10,b=120;/*外部变量定义*/运行结果:max=120{25由于外部变量定义在main函数之后,因此在main函数引用外部变量a和b之前,应该用extern进行外部变量说明,说明a、b是外部变量。如果不作extern说明,编译时出错,系统不会认为a、b是已定义的外部变量。一般的做法是外部变量的定义放在引用它的所有函数之前,这样可以避免在函数中多加一个extern说明。外部变量的定义与外部变量的说明并不是一回事。外部变量的定义只能有一次,它的位置在所有函数之外,而同一文件中的外部变量的说明可以有多次,它的位置在函数之内。(4)如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。例7.4求两个数的最大值。inta=3,b=5;/*a、b为外部变量*/a、b的作用范围max(a,b)由于外部变量定义在main函数之后,因此在main函26inta,b;/*a、b为局部变量*/{intc;c=a>b?a:b;return(c);

}main(){inta=10;/*a为局部变量*/printf(″max=%d\n″,max(a,b));}运行结果:max=10形参a、b作用范围

局部变量a作用范围全局变量b作用范围inta,b;/*a、b为局部变量*/形参27第一行定义了外部变量a、b,并使之初始化。第二行开始定义函数max,a、b是形参,形参也是局部变量。函数max中的a、b不是外部变量a、b,它们的值是由实参传给形参的,外部变量a、b在max函数范围内不起作用。最后4行是main函数,定义了一个局部变量a,因此全局变量a在main函数范围内不起作用,而全局变量b在此范围内有效。因此printf函数中的max(a,b)相当于max(10,5),程序运行后得到结果为10。7.6.1变量的存储类别从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。从变量值存在时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式。7.6动态存储方式与静态存储方式第一行定义了外部变量a、b,并使之初始化。第二行开始28

1.静态存储方式是指在程序运行期间分配固定的存储空间的方式。

2.动态存储方式是指在程序运行期间根据需要进行动态的分配存储空间的方式。供用户使用的存储空间可分为三部分:(1)程序区;

(2)静态存储区;(3)动态存储区。数据分别存放在静态存储区和动态存储区中。全局变量存放在静态存储区中。在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地分配和释放。1.静态存储方式29局部变量存放在动态存储区中,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。在程序执行过程中,这种分配和释放是动态的。在动态存储区中存放以下数据:①函数形参变量。在调用函数时,给形参变量分配存储空间.②局部变量。未加static说明的局部变量,即自动变量。③函数调用时的现场保护和返回地址等。对以上这些数据,在函数调用开始时分配动态存储空间,函数结束时,释放这些空间。程序运行过程中,这种分配和释放是动态的,如果在一个程序中两次调用同一函数,分配给此函数中局部变量的存储空间地址可能是不同的。如果一个程序包含了若干个函数,每个函数中的局部变量的生存期并不等于整个程序的执行周期,它只是程序执行周期的一部分。根据函数调用的需要,动态分配和释放存储空间。局部变量存放在动态存储区中,在函数调用开始时分配动态30在C语言中每一个变量和函数有两个属性:数据类型和数据的存储类别。存储方法分为两大类:静态存储类和动态存储类。具体包括4种:自动(auto);静态(static);寄存器(register);外部(extern)。7.6.2局部变量的存储方式

1.自动存储类别函数中的局部变量,如不为static存储类别都是动态分配存储空间的,存储在动态存储区中,分配和释放存储空间的工作由编译系统自动处理,因此这类局部变量称为自动变量。自动变量用关键字auto作存储类型说明。例如:当局部变量未指明存储类别时,被定义为auto存储类别。intf(a)/*定义函数*/inta;/*定义a为形参*/在C语言中每一个变量和函数有两个属性:数据类型和数据31{autointb,c=9;/*定义b、c为自动变量*/…

b、c是自动变量,c=9,执行完f函数后自动释放其所占的存储单元。

auto也可以省略不写,则隐含确定为“自动存储类别”,它属于动态存储类别。autointb,c=9;与intb,c=9;是等价的。

2.静态存储类别存储分类符static既可用于说明全局变量,也可用于说明局部变量。当其作用于局部变量时,该变量称为局部静态变量;其作用于全局变量时,该变量称为外部静态变量。

(1)局部静态变量{32在函数体内用static说明的变量称为静态局部变量。在程序运行期间,它占据一个永久性的存储单元,在退出函数后,值仍旧保留。静态变量是在编译时赋初值,因此在程序执行期间,一旦存储单元中的值改变,就不会再执行赋初值语句。未赋初值的变量,C编译程序将其置为0。形参不允许说明为静态存储类别。例7.6main(){f1();f1();f1();

}运行结果:x=1

f1(){intx=0;x=x+1;printf(″x=%d\n″,x);}在函数体内用static说明的变量称为静态局部变量。33x=1x=1若把intx=0;改为:staticintx=0;运行结果如下:x=1x=2x=3

从程序可以看出,自动变量在函数每次被调用时,都进行初始化。而静态变量只在编译阶段初始化一次。在上例中,前一种情况x是自动变量,f1()函数每次被调用时,x都初始化,因此,输出结果始终是1。后一种情况x是局部静态变量,f1()函数第一次被调用时,x为0,退出时为1。第二次调用时,x的值是1,仍然存在,因此,输出时x值为2,同样,第三次调用时,x的值为3。

(2)外部静态变量x=134当定义一全局变量时,若将其指定为static类,则说明该全局变量只在本源程序文件中使用,其他的源程序文件不能引用该全局变量。这种外部静态变量主要用于同一程序分别由多人编写完成时的情况,避免不同编程者定义出相同名字的全局变量。对于全局变量,不管是否加static说明,均属于静态存储变量。使用static只是为了限制其引用范围。

3.寄存器存储类别为了提高程序的执行效率,C语言允许将局部变量的值放在CPU的通用寄存器中,这种变量称为寄存器变量。寄存器变量用关键字register说明。例如,函数体中的变量说明:registerinta,b;定义了两个寄存器变量a、b,函数运行时将尽可能把a、b的值放在寄存器中。当定义一全局变量时,若将其指定为static类,则说35在计算机中,从内存“存取”数据要比直接从寄存器中“存取”数据慢,所以对一些使用特别频繁的变量,可通过register将其定义成寄存器变量,使程序直接从寄存器中“存取”数据,以提高程序的效率。由于计算机的寄存器数目有限,并且不同的计算机系统允许使用寄存器的个数不同,所以不宜定义太多的寄存器变量,只有将少量变化频繁的变量定义成寄存器变量,如循环控制变量等。当一函数内定义的寄存器变量的个数超过系统所允许使用的寄存器数时,系统将自动将其作为一般局部变量处理,即仍使用内存单元存放其值,并不提高执行速度。

说明:

(1)只有局部自动变量和形式参数可说明为寄存器变量。(2)一个计算机系统中的寄存器的数目是有限的。(3)不同系统对register的处理不同。在计算机中,从内存“存取”数据要比直接从寄存器中“存36(4)局部静态变量不能定义为寄存器变量,不能写成:registerstatica,b,c;7.6.3全局变量的存储方式

全局变量可以使用extern和static存储类别。当未对全局变量指定存储类别时,隐含为extern类别。

1.外部存储类别当在一个文件中要引用另一个文件中的全局变量或在全局变量之前要引用它时,可用extern说明。相当于扩大全局变量的作用域。例7.7f1.c程序文件

main(){intpower();externinta;

intb=3,c,d,m;(4)局部静态变量不能定义为寄存器变量,不能写成:37printf(″a,m=?\n″);scanf(″%d,%d″,&a,&m);c=a*b;printf(″%dˇ%d=%d\n″,a,b,c);d=power(m);printf(″%dˇ%d=%d\n″,a,m,d);}inta;f2.c程序文件

externinta;power(n)intn;

{inti,y=1;printf(″a,m=?\n″);38for(i=1;i<=n;i++)y*=a;return(y);}程序文件f1中的最后一行为对全局变量a的定义,main函数在其作用域之外,所以在main函数中对其进行了外部说明,而在文件f2中的第一行也是对a进行说明。由于全局变量在整个程序的运行过程中“永久性”地占用固定的存储单元,所以它存放在静态数据区中,属于静态存储变量.

2.静态存储类别由static说明的全局变量称为静态全局变量,它只能由本文件引用,即使在其他文件中用extern说明也不能使用,相当于限制了全局变量作用域的扩展。7.6.4存储类别小结for(i=1;i<=n;i++)39对一个数据的定义,需要指定两种属性:数据类型和存储类别,分别用两个关键字进行定义,如:staticinta;(静态内部变量或静态外部变量)autocharc;(自动变量,在函数内定义)

registerintd;(寄存器变量,在函数内定义)externintb;(定义外部变量)从不同角度做以下归纳。(1)从作用范围角度分:有局部变量和全局变量。它们采取的存储类别如下:

自动变量,即动态局部变量(离开函数,值就消失)静态局部变量(离开函数,值仍然保留)寄存器变量(离开函数,值就消失)(形式参数可以定义为自动变量或寄存器变量)局部变量对一个数据的定义,需要指定两种属性:数据类型和存储类40

静态外部变量(只限本文件引用)外部变量(即非静态的外部变量,允许其他文件引用)

(2)从变量存在的时间来区分,有动态存储和静态存储两种类型。

自动变量(本函数内有效)寄存器变量(本函数内有效)形式参数(本函数内有效)静态局部变量(函数内有效)静态外部变量(本文件内有效)外部变量(其他文件可引用)(3)从变量值存放的位置来区分:

静态局部变量静态外部变量(函数外部静态变量)外部变量(可为其他文件引用)全局变量动态存储静态存储内存中静态存储区静态外部变量(只限本文件引用)全局变量动41

内存中动态存储区:

自动变量和形式参数

CPU中的寄存器:

寄存器变量7.6.5内部函数和外部函数

1.内部函数内部函数又称静态函数。定义时被说明成static类别,静态函数只局限于所在文件,其他文件不能调用。

2.外部函数定义时被说明成extern类别的函数为外部函数。函数的隐含类别为extern类别,外部函数可以被其他文件调用。7.7.1内部函数7.7函数的存储分类内存中动态存储区:自动变量和形式参数7.7函数的42当定义一个函数时,若在函数返回值的类型前加上static时,则称此函数为“静态”函数。

格式

static类型标识符函数名(形参表)静态函数的特征只限于本文件中的其他函数调用,所以又称作“内部”函数。使用内部函数,可以避免不同文件因函数同名而引起混乱。通常把只能由同一文件使用的内部函数和外部变量放在一个文件中,在它们前面加上static使之局部化,其他文件不能引用。

例7.8file1.c(文件1)main(){inta,b;externvoidswap()当定义一个函数时,若在函数返回值的类型前加上stat43

scanf(″%d%d″,&a,&b);if(a<b)swap(a,b);printf(″%d,%d\n″,a,b);}file2.c(文件2)

staticvoidswap(x,y)intx,y;{

intt;t=x;x=y;y=t;}

file2.c中的函数swap是内部函数,只能被file2.c中的其他函数调用。在file1.c的main函数中,虽然用extern说明了swap是外部函数,但这是徒劳的。连接时会产生出错信息。scanf(″%d%d″,&a,&b447.7.2外部函数在定义函数时,如果在函数名和函数类型的前面加上关键字extern,则表示此函数是外部函数,可供其他文件调用。

格式

extern类型标识符函数名(形参表)

C语言规定,如果在定义函数时省略extern,则隐含为外部函数。通常,当函数调用语句与被调函数不在同一文件,且函数的返回值为非整型时,应在调用语句所在函数的说明部分用extern说明所调用函数。例7.9file1.c(文件1)

externfloatadd();main(){floata,b;scanf(″%f%f″,&a,&b);7.7.2外部函数45printf(″a=%f\nb=%f\ntotal=%f\n″,a,b,add

(a,b));}…./*file1.c的尾部*/file2.c(文件2)floatadd(x,y)floatx,y;

{return(x+y);}在文件file1.c的main函数中要调用add函数,而add函数在file2.c中。因此在main函数的前面用externfloatadd()来说明,这时,函数add的作用域扩展到file1.c文件的末尾。printf(″a=%f\nb=%f\n46例7.10输出1到5的阶乘值。

#include<stdio.h>intfac(int

n)

if(n>1)f=n*fac(n-1)elsef=1;return(f);

main(){inti;for(i=1;i<=5;i++)7.8程序设计举例例7.10输出1到5的阶乘值。7.8程序设计47printf(″%d!=%d\n″,i,fac(i));}运行结果为

1!=12!=23!=64!=245!=120

printf(″%d!=%d\n″,i,fa48题7.1编写程序,求s=s1+s2+s3+s4

的值,其中:s1=1+1/2+1/3+…+1/50s2=1+1/2+1/3+…+1/100s3=1+1/2+1.3+…+1/150s4=1+1/2+1/3+…+1/200

此题目可以首先编一函数,用于求1+1/2+1/3+…+1/n的值,然后通过函数调用求s的值。题7.2利用选择排序法,对数组a中的10个整数按从小到大的顺序排列,并将排序结果显示出来。选择排序法的基本思想是:先将a[0]到a[9]中的最小数与a[0]对换;再将a[1]到a[9]中的最小数与a[1]对换;依此类推,直到排序完成为止。7.9程序设计题目题7.1编写程序,求s=s1+s2+s3+s4的49谢谢骑封篙尊慈榷灶琴村店矣垦桂乖新压胚奠倘擅寞侥蚀丽鉴晰溶廷箩侣郎虫林森-消化系统疾病的症状体征与检查林森-消化系统疾病的症状体征与检查谢谢骑封篙尊慈榷灶琴村店矣垦桂乖新压胚奠倘擅寞侥蚀丽鉴晰溶廷50第7章函数与变量7.1函数概述7.2函数参数和返回值7.3函数的调用7.4数组的作为函数参数7.5变量的定义位置和作用域7.6动态存储方式与静态存储方式7.7函数的存储分类7.8程序设计举例7.9程序设计题目第7章函数与变量7.1函数概述51

C语言函数分为两种:

标准函数和用户自定义的函数。

标准函数是系统提供的已设计好的函数,可以直接调用,用户自己定义的函数是用户自己编写的用来解决具体问题的函数.(1)C程序的执行从main函数开始,调用其他函数后流程回到main函数。(2)所有函数都是平行的,即在定义函数时,是互相独立的,一个函数并不从属于另一函数,即函数不能嵌套定义,但可以互相调用,但不能调用main函数。(3)从用户使用的角度看,函数分两种:①标准函数,即库函数。这是由系统提供的,用户不用自己定义。②用户自己定义的函数,以解决用户的专门问题。7.1函数概述7.1函数概述52(4)从函数的形式看,函数分两类:

①无参函数:主调函数并不将数据传送给被调用函数。

②有参函数:在调用函数时,在主调函数和被调用函数之间有参数传递,也就是说,主调函数可以将数据传给被调用函数使用,被调用函数中的数据也可以带回来供主调函数使用。7.1.2函数的定义

1.无参函数的定义

类型标识符函数名(){说明部分或语句}

说明:

用“类型标识符”指定函数值的类型,即函数带回来的值的类型。C语言默认返回值的类型是整型。2.有参函数的定义

类型标识符函数名(形式参数说明){说明部分语句}(4)从函数的形式看,函数分两类:类型标识符函数名53

类型标识符函数名(形式参数表列)形式参数说明{说明部分语句}例如:intmax(x,y)

intx,y;/*形式参数说明*/{intz;/*函数体中的说明部分*/z=x>y?x:y;return(z);/*z为函数的返回值*/

}这是一个求x和y二者中大者的函数,x和y为形式参数,由主调函数的实际参数把参数值传递给被调用函数中的形式参数x和y。类型标识符函数名(形式参数表列)547.2.1函数参数在调用函数时,大多数情况下,主调函数和被调函数之间有数据传递关系。在定义函数时,函数名后面括号中变量名称为“形式参数”,简称“形参”。在调用函数时,函数名后面括号中的表达式称为“实际参数”,简称“实参”。(1)在定义函数中指定的形参变量,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时函数max中的形参才被分配内存单元。调用结束后,形参所占用的内存单元也同时被释放。(2)实参可以是常量、变量或表达式,如:max(3,a+b);但要求它们有确定的值。在调用时将实参的值赋给形参变量。7.2函数参数和返回值7.2.1函数参数7.2函数参数和返回值55(3)在定义的函数中,必须指定形参的类型。(4)实参与形参的类型应一致。只有字符型与整型可以互相通用。(5)实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。7.2.2函数的返回值函数的返回值是由return语句传递的。

格式:return(表达式);或return表达式;

功能:用return语句从函数退出,返回到调用它的程序中.该语句有两重作用:(1)从函数中退出,返回到调用它的程序中。(2)向调用程序返回一个值。(3)在定义的函数中,必须指定形参的类型。56

7.3.1函数调用的一般形式格式:函数名(实参表列);

函数调用语句的执行过程:首先计算每个实参表达式的值,并把此值存入所对应的形参单元中,然后把执行流程转入函数体中,执行函数体中的语句,函数体执行完之后,将返回到调用此函数的程序中的下一条语句,继续去执行。当执行到函数体的右花括号或return语句时,表示函数体执行完成,这时将返回到主调程序中。

7.3函数的调用7.3函数的调用577.3.2对被调用函数的说明在一个函数中调用另一个被调函数,需要具备的条件:

(1)首先被调函数必须是已经存在的函数(库函数或用户自定义函数)。(2)如果使用库函数,一般还应在本文件开头用#include命令将调用有关库函数时所需用的信息包含到本文件中来。①输入输出库函数:#include<stdio.h>——使用系统定义的标准输入输出函数。②数学库函数:#include″math.h″———使用系统定义的标准数学运算函数。(3)如果使用用户自己定义的函数,而且该函数与调用它的函数(即主调函数)在同一个文件中,一般还应该在主调函数中对被调用函数的返回值的类型作说明。7.3.2对被调用函数的说明58这种类型说明的一般形式为:类型标识符被调函数函数名();

对被调用函数的说明,在以下几种情况下可以省略:

(1)如果函数的值(函数的返回值)是整型或字符型,可以不进行说明,系统对它们自动按整型说明。(2)如果被调用函数的定义出现在主调函数之前,可以不进行说明,因为编译系统已经先知道了已定义的函数类型,会自动处理。(3)如果在所有函数定义之前,对函数类型进行了说明,则在各个主调函数中不再进行说明。

7.3.4函数的嵌套调用C语言的函数定义都是互相平行、独立的,可以嵌套调用函数即在调用一个函数的过程中,又调用另一个函数.

这种类型说明的一般形式为:597.3.5函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数递归调用。例如:intf(x)intx;{inty,z;…z=f(y);…return(2*z);

}7.3.5函数的递归调用607.4.1数组元素做函数实参由于实参可以是表达式,数组元素又可以是表达式的组成部分,因此数组元素可以作为函数的实参,与变量作实参一样,是单向传递,即“值传送”。只能将数组元素的值传递给被调函数的形参,不能带回变化的值。这种方式适合部分数组元素传递和元素较小的数组传递。

7.4数组的作为函数参数7.4数组的作为函数参数61运行结果:形参值为:234实参值为:1237.4.2用数组名做函数的实参与形参例7.1有一个一维数组s,存放10个学生成绩,求平均成绩.

floataverage(a)/*数组名a作形参*/floata[10];{inti;floatv,sum=a[0];for(i=1;i<10;i++)sum=sum+a[i];v=sum/10;return(v);

}运行结果:形参值为:23462main(){floats[10],v;inti;printf(″input10s:\n″);for(i=0;i<10;i++)scanf(″%f″,&s[i]);printf(″\n″);v=average(s);/*数组名s作实参*/printf(″averageis%5.2f″,v);}运行结果:input10s:

100567898.576879967.57597≠averageis83.40main()63

说明:

(1)用数组名做函数参数,应该在主调函数和被调函数中分别定义数组。(2)实参数组与形参数组类型应一致。(3)实参数组和形参数组大小可以一致也可以不一致,C编译时对形参数组大小不作检查,只是将实参数组的首地址传给形参数组。若要求形参数组得到实参数组全部的元素值,则应当指定形参数组与实参数组大小一致。也可以不指定形参数组大小,在定义数组名后面跟一个空的方括弧,为了在被调函数中处理数组元素的需要,可以另设参数,说明传递数组元素的个数。例7.2floataverage(a,n)/*a为形参数组名,n用于说明数组元素个数*/floata[];说明:64intn;{

inti;floatv,sum=a[0];for(i=1;i<n;i++)sum=sum+a[i];v=sum.n;return(v);}main(){

floatscore1[5]={98.5,97,91.5,60,55};floatscore2[8]={67,88.5,79,45,90,81,77.5,99};intn;65printf(″theaverageofclass1is%6.2f\n″,average(score1,5));/*调用时5为形参数组a的元素个数*/printf(″theaverageofclass2is%6.2f\n″,average(score2,8));}运行结果:theaverageofclass1is80.40theaverageofclass2is78.37这么处理数组的大小就可以不同。(4)数组名做函数参数时,把实参数组的起始地址传递给形参数组,这样两个数组就共占同一段内存单元。如图7.1所示.假如a数组的起始地址为1000,则s数组的起始地址也为1000,显然,a和s同占一段内存单元。a[0]与s[0]同占一个单元…。printf(″theaverageo66这种传递方式叫“地址传递”。由此可以看到,形参数组中各元素的值发生变化,会使实参数组元素的值同时发生变化。这一点与变量函数参数的情况不同。

a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]2468101214161820s[0]s[1]s[2]s[3]s[4]s[5]s[6]s[7]s[8]s[9]C语言中变量的定义有三个基本位置:函数内部、函数参数中及所有函数外部。这些变量分别称为局部变量、形式参数变量、全局变量。定义位置不同,变量的作用域不同,即变量所起作用的范围不同。有关形式参数已经介绍过。图7.1用数组名做参数时,形参与实参的存储关系7.5变量的定义位置和作用域这种传递方式叫“地址传递”。由此可以看到,形参数组中677.5.1局部变量局部变量又称做内部变量,是在函数内部定义的变量。形参也是局部变量。其作用域是从定义的位置起,到函数体结束止。也就是说,只能在定义该变量的函数内使用它,在此函数以外不能使用。对于局部变量,它只在进入本函数时生成,在退出该函数时消失。因此在C语言中不同的函数内可以定义相同名字的变量,它们代表的对象不同,互不影响。例如:f1(){inta,b;a=5;b=10;…}局部变量a,b的作用域7.5.1局部变量局部变量a,b的作用域68f2(){inta,b;a=100;b=-200;…}f1()和f2()中的变量a、b均为局部变量,并且互不相关。其原因是每个a、b作为局部变量仅在被定义的函数内是可知的。在复合语句中也可以定义本段程序的局部变量。例如:局部变量a,b的作用域f2()局部变量a,b的作用域69f(x)intx;{inta;…if(x>0){

intb=40;…

…}b的作用域仅在其定义的分程序之内,即进入分程序时建立b变量,结束分程序时立即撤消。b的作用域x、a的作用域f(x)b的作用域x、a的作用域707.5.2全局变量全局变量又称做外部变量,是在函数外部定义的变量。其有效范围是从变量定义的位置开始到本源文件结束止。若在同一个源文件中,局部变量与全局变量同名,则在局部变量的作用范围内,全局变量被屏蔽,不起作用。在函数体外进行的函数说明也使该函数具有全局的性质。7.5.2全局变量71inta=1,b=5;floatf1(x)intx;

{intc,d;…

}charc1,c2;charf2(x,y)intx,y;{inti,j;…}main(){intm,n;…

}全局变量c1、c2作用域全局变量a、b作用域inta=1,b=5;全局变量c1、c2作用域全局72a、b、c1、c2都是全局变量,但它们的作用范围不同。在main函数和函数f2中可以使用全局变量a、b、c1、c2,但在函数f1中,只能使用全局变量a、b,而不能使用c1和c2。

说明:

(1)全局变量的作用:增加函数间数据联系的渠道。由于同一文件中的所有函数都能引用全局变量的值,因此,如果在一个函数中改变了全局变量的值,就能影响到其他函数,相当于各个函数间有直接的传递通道。由于函数的调用只能带回一个返回值,因此有时可以利用全局变量增加与函数联系的渠道,从函数得到一个以上的返回值。对于全局变量,如果在定义时不进行初始化,则系统将自动赋予其初值,对数值型赋0,对于字符型赋空′\0′。

a、b、c1、c2都是全局变量,但它们的作用范围不同73(2)使用全局变量会增加程序的内存开销,因为全局变量在程序的整个执行过程中都有效,即一直占用着内存单元,而不是像局部变量那样,在进入其所在函数时才开辟存储单元,退出函数时便将其释放。使用全局变量,还会降低函数通用性,而且会降低程序的清晰度。建议不要无限制地使用全局变量。(3)在定义全局变量时,最理想的定义位置是在源文件的开头处,这样,在整个文件中的所有函数均可使用该变量。如果将一全局变量定义在源程序文件的中间,则其前面的函数不能使用该变量。如果在定义点之前的函数想引用该外部变量,则应该在该函数中用关键字extern作“外部变量说明”。表示该变量在函数的外部定义,在函数内部可以使用它们。见例7.10.例7.3求两个数的最大值。

intmax(x,y)intx,y;(2)使用全局变量会增加程序的内存开销,因为全局变量在74{

intz;z=x>y?x:y;return(z);}main(){externinta,b;/*外部变量说明*/printf(″max=%d\n″,max(a,b));

}inta=10,b=120;/*外部变量定义*/运行结果:max=120{75由于外部变量定义在main函数之后,因此在main函数引用外部变量a和b之前,应该用extern进行外部变量说明,说明a、b是外部变量。如果不作extern说明,编译时出错,系统不会认为a、b是已定义的外部变量。一般的做法是外部变量的定义放在引用它的所有函数之前,这样可以避免在函数中多加一个extern说明。外部变量的定义与外部变量的说明并不是一回事。外部变量的定义只能有一次,它的位置在所有函数之外,而同一文件中的外部变量的说明可以有多次,它的位置在函数之内。(4)如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。例7.4求两个数的最大值。inta=3,b=5;/*a、b为外部变量*/a、b的作用范围max(a,b)由于外部变量定义在main函数之后,因此在main函76inta,b;/*a、b为局部变量*/{intc;c=a>b?a:b;return(c);

}main(){inta=10;/*a为局部变量*/printf(″max=%d\n″,max(a,b));}运行结果:max=10形参a、b作用范围

局部变量a作用范围全局变量b作用范围inta,b;/*a、b为局部变量*/形参77第一行定义了外部变量a、b,并使之初始化。第二行开始定义函数max,a、b是形参,形参也是局部变量。函数max中的a、b不是外部变量a、b,它们的值是由实参传给形参的,外部变量a、b在max函数范围内不起作用。最后4行是main函数,定义了一个局部变量a,因此全局变量a在main函数范围内不起作用,而全局变量b在此范围内有效。因此printf函数中的max(a,b)相当于max(10,5),程序运行后得到结果为10。7.6.1变量的存储类别从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。从变量值存在时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式。7.6动态存储方式与静态存储方式第一行定义了外部变量a、b,并使之初始化。第二行开始78

1.静态存储方式是指在程序运行期间分配固定的存储空间的方式。

2.动态存储方式是指在程序运行期间根据需要进行动态的分配存储空间的方式。供用户使用的存储空间可分为三部分:(1)程序区;

(2)静态存储区;(3)动态存储区。数据分别存放在静态存储区和动态存储区中。全局变量存放在静态存储区中。在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地分配和释放。1.静态存储方式79局部变量存放在动态存储区中,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。在程序执行过程中,这种分配和释放是动态的。在动态存储区中存放以下数据:①函数形参变量。在调用函数时,给形参变量分配存储空间.②局部变量。未加static说明的局部变量,即自动变量。③函数调用时的现场保护和返回地址等。对以上这些数据,在函数调用开始时分配动态存储空间,函数结束时,释放这些空间。程序运行过程中,这种分配和释放是动态的,如果在一个程序中两次调用同一函数,分配给此函数中局部变量的存储空间地址可能是不同的。如果一个程序包含了若干个函数,每个函数中的局部变量的生存期并不等于整个程序的执行周期,它只是程序执行周期的一部分。根据函数调用的需要,动态分配和释放存储空间。局部变量存放在动态存储区中,在函数调用开始时分配动态

温馨提示

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

评论

0/150

提交评论