《C语言程序设计》第七章-函数课件_第1页
《C语言程序设计》第七章-函数课件_第2页
《C语言程序设计》第七章-函数课件_第3页
《C语言程序设计》第七章-函数课件_第4页
《C语言程序设计》第七章-函数课件_第5页
已阅读5页,还剩139页未读 继续免费阅读

下载本文档

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

文档简介

7.1函数7.2函数的定义7.3函数的参数和返回值7.4函数的调用7.5函数的嵌套与递归调用7.6数组与函数参数7.7指针与函数7.8变量的存储类别7.9模块化程序设计方法补充:如何运行一个多文件的程序

第七章函数与模块化程序设计方法7.1函数第七章函数与模块化程序设计方法

一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能.一个C程序可由一个主函数和若干个函数构成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。下图是一个程序中函数调用的示意图:maingedefghhicba函数概述

一个较大的程序一般应分为若干个程序模块,每一main()/*主函数*/{

print_star();/*调用print_star函数画****/

print_message();/*调用print_message函数写字*/

print_star();/*调用print_star函数画****/}print_star()/*定义print_star函数*/{

prinf(“\n**********”);}print_message()/*定义print_message函数*/

{prinf(“\nHello!”);}函数调用实例:运行结果:**********

Hello!**********

main()说明:(1)一个源程序文件由一个或多个函数组成。一个源程序文件是一个编译单位,即以源程序为单位进行编译,而不是以函数为单位进行编译。(2)一个C程序由一个或多个源程序文件组成。一个源文件可以为多个C程序公用。(3)C程序的执行从main函数开始,调用其他函数后流程回到

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

{声明部分语句

}2、有参函数定义的一般形式

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

{声明部分语句

}

函数的定义1、无参函数的定义形式类型标识符函数名()2、有参函数定例如:

intmax(intx,inty){intz;z=x>y?x:y;return(z);}例如:3、可以有“空函数”它的形式为:

类型说明符函数名()

{}例如:

dumy(){}3、可以有“空函数”它的形式为:例如:4、对形参的声明的传统方式

在老版本C语言中,对形参类型的声明是放在函数定义的第2行,也就是不在第l行的括号内指定形参的类型,而在括号外单独指定,例如:intmax(x,y)intx,y;{intz;z=x>y?x:y;return(z);}

一般把这种方法称为传统的对形参的声明方式,而把前面介绍过的方法称为现代的方式。TurboC和目前使用的多数C版本对这两种方法都允许使用,两种用法等价。4、对形参的声明的传统方式在老版本C语言中,对形1、形式参数和实际参数

在定义函数时函数名后面括弧中的变量名称为“形式参数”,在主调函数中调用一个函数时,函数名后面括弧中的参数(可以是一个表达式)称为“实际参数”。函数的参数和返回值

1、形式参数和实际参数在定义函数时函数名后面例:调用函数时的数据传递main(){inta,b,c;scanf(“%d%d”,&a,&b);c=max(a,b);printf(“Maxis%d”,c);}intmax(intx,inty){intz;z=x>y?x:y;return(z);}运行情况:

7,8Maxis8例:调用函数时的数据传递main()运行情况:(1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数

max中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。(2)实参可以是常量、变量或表达式,如:

max(3,a+b);

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

语句将被调用函数中的一个确定值带回主调函数中去。

return

语句后面的括弧也可以不要,如:

returnz;return后面的值可以是一个表达式。

int

max(intx,inty){return(x>y?x:y);}函数的返回值(1)函数的返回值是通过函数中的return语句(2)函数值的类型。在定义函数时指定函数值的类型。例如:intmax(floatx,floaty)

/★函数值为整型★/charletter(charc1,charc2)

/★函数值为字符型★/doublemin(intx,inty)

/★函数值为双精度型★/(2)函数值的类型。在定义函数时指定函数值的类型。例如:in

C语言规定,凡不加类型说明的函数,一律自动按整型处理。在定义函数时对函数值说明的类型一般应该和return语句中的表达式类型一致。例返回值类型与函数类型不同。main(){floata,b;intc;scanf(“%f,%f,”,&a,&b);c=max(a,b);printf(“”Maxis%d\n”,c);}

max(floatx,floaty);{floatz;/★z为实型变量★/z=x>y?x:y;return(z);}运行情况如下:1.5,2.5Maxis2C语言规定,凡不加类型说明的函数,一律自动按(3)如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。(4)如果被调用函数中没有return语句,并不带回一个确定的、用户所希望得到的函数值,但实际上,函数并不是不带回值,而只是不带回有用的值。带回的是一个不确定的值。(5)为了明确表示“不带回值”,可以用“void”定义“无类型”(或称“空类型”)(3)如果函数值的类型和return语句中表达式的值不一致,1、函数调用的一般形式

函数名(实参表列);

如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略,如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应一致。实参与形参按顺序对应,一一传递数据。如果实参表列包括多个实参,对实参求值的顺序并不是确定的,有的系统按自左至右顺序求实参的值,有的系统则按自右至左顺序。

许多

C版本(例如

Turbo

C和

MSC)是按自右而左的顺序求值。

函数的调用

1、函数调用的一般形式函数名(实参表列);2、函数调用的方式

按函数在程序中出现的位置来分,可以有以下三种函数调用方式:(1)函数语句

把函数调用作为一个语句。如:

printstar();2、函数调用的方式按函数在程序中出现的位置来分(2)函数表达式

函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参加表达式的运算。例如:

c=2*max(a,b);(3)函数参数函数调用作为一个函数的实参。例如:m=max(a,max(b,c));(2)函数表达式函数出现在一个表达式中,这种3、对被调用函数的声明和函数原型

在一个函数中调用另一函数(即被调用函数)需要具备的条件(1)首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。(2)如果使用库函数,一般还应该在本文件开头用#include命令将调用有关库函数时所需用到的信息“包含”到本文件中来。例如,前几章中已经用过的

#include<stdio.h>(3)如果使用用户自己定义的函数,而且该函数与调用它的函数(即主调函数)在同一个文件中,一般还应该在主调函数中对被调用的函数作声明,即对编译系统声明将要调用此函数,并将有关信息通知编译系统。3、对被调用函数的声明和函数原型在一个函数中调用另一函数(例对被调用的函数作声明main(){floatadd(floatx,floaty);/★对被调用函数的声明★

/floata,b,c;scanf(“%f,%f”,&a,&b);c=add(a,b);printf(“sumis%f”,c);}floatadd(floatx,floaty);/

★函数首部★

/{floatz;/

★函数体★

/z=x+y;return(z);}运行情况如下:3.5,6.5sumis10.000000例对被调用的函数作声明main()运行情况如下:在函数声明中也可以不写形参名,而只写形参的类型。如:

floatadd(float,float);在C语言中,以上的函数声明称为函数原型(functionprototype)。它的作用主要是利用它在程序的编译阶段对调用函数的合法性进行全面检查。函数原型的一般形式为①函数类型函数名(参数类型1,参数类型2……)②函数类型函数名(参数类型1参数名1,参数类型2

参数名2,……)

第①种形式是基本的形式。为了便于阅读程序,也允许在函数原型中加上参数名,就成了第②种形式。但编译系统不检查参数名。因此参数名是什么都无所谓。在函数声明中也可以不写形参名,而只写形参的类型。如:在C语言说明:①

如果在函数调用之前,没有对函数作声明,则编译系统会把第一次遇到的该函数形式(函数定义或函数调用)作为函数的声明,并将函数类型默认为int型。

如果函数类型为整型,可以在函数调用前不必作函数声明。但是使用这种方法时,系统无法对参数的类型做检查。若调用函数时参数使用不当,在编译时也不会报错。因此,为了程序清晰和安全,建议都加以声明为好。②

如果被调用函数的定义出现在主调函数之前,可以不必加以声明。说明:①如果在函数调用之前,没有对函数作声明,则编译系统会③

如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必对所调用的函数再作声明。例如:charletter(char,char);/★以下3行在所有函数之前,且在函数外部★

/floatf(float,float);inti(float,float);main(){…}/

★不必声明它所调用的函数★

/charletter(charc1,charc2)/

★定义letter函数★

/{…}floatf(floatx,floaty)/

★定义f函数★

/{…}inti(floatj,floatk)/

★定义i函数★

/{…}③如果已在所有函数定义之前,在函数的外部已做了函数声cha例题:写出下列程序的输出结果

(1)#include<stdio.h>swap(inta,intb){inttemp;temp=a;a=b;b=temp;}main(){intx=7,y=11;printf("x=%d,\ty=%d\n",x,y);printf("swapped:\n");swap(x,y);printf("x=%d,\ty=%d\n",x,y);}答案:x=7 y=11swapped:x=7y=11例题:写出下列程序的输出结果

(1)#include<s(2)voidswap(int*p1,int*p2){intp;p=*p1;*p1=*p2;*p2=p;}main(){intx=7,y=11;printf(“x=%d,y=%d\n”,x,y);printf(“swapped:\n”);swap(&x,&y);printf(“x=%d,y=%d\n”,x,y);}答案:

x=7y=11swapped:x=11y=7(2)voidswap(int*p1,int*p2)分析:

通过对上面两程序,理解两种参数传递方式的区别:

•值传递方式函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值。特点是形参与实参占用不同的内存单元,单向传递。

•地址传递函数调用时,将数据的存储地址作为参数传递给形参,特点是形参与实参占用同样的存储单元,“双向”传递,实参必须是地址常量或变量,形参是地址变量。分析:

通过对上面两程序,理解两种参数传递方式的区别:

C语言的函数定义都是互相平行、独立的,也就是说在定义函数时,一个函数内不能包含另一个函数。

C语言不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。main函数a函数b函数调用a函数调用b函数结束①②③④⑤⑥⑦⑧⑨函数的嵌套调用

C语言的函数定义都是互相平行、独立的,也就例编一个程序计算下列函数值;

要求为函数p(i)、s(n)、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!ni=1例编一个程序计算下列函数值;要求为函数p(i)floatp(longi){longk,j;for(k=j=1;j<=i;j++)k=k*j;return((float)(k));}floats(longn){floatsum=0.0;longk;for(k=1;k<=n;k++)sum=sum+p(k);return(sum);}floatf(longx,longy){floatf1;f1=s(x)/s(y);return(f1);}main(){longx1,y1;scanf(“%1d,%1d”,&x1,&y1);printf(“f(x,y)=%f\n”,f(x1,y1));}floatp(longi)floatf(longx,

在调用一个函数的过程中又出现直接或间接调用该函数本身,称为函数的递归调用。

1、递归调用的定义:

2、例:编一个计算n!(n>1)的递归调用函数。

floatp(longn){if(n<1L){printf(“inputerror!\n”);return(-1);}elseif(n==1L)return(1);elsereturn(n*p(n-1));}

函数的递归调用

在调用一个函数的过程中又出现直接或间接调用该函数1、如果有一条调用语句“k=p(4);”,用图示的方式描述其执行过程。返回p(4)的值为24返回p(3)的值为6返回p(2)的值为2返回p(1)的值为1要计算4*p(3)要计算2*p(1)要计算3*p(2)有返回值为1k=p(4);对求阶乘的递归函数来说,这两个条件可以写成下列公式:递归计算公式

p(n)=n*p(n-1)递归结束条件

p(1)=1

例:对求阶乘的递归函数来说,这两个条件可以写成下列公式P(n)=n*p(n-1)n>11n=0,1如果有一条调用语句“k=p(4);”,用图示的方式描述其执行4、例Hanoi塔问题

古代有一个梵塔,塔内有三个座a、b、c,开始时a座有三个盘子,盘子大小不等,大的在下,小的在上,要把这三个盘子从a座移到c座,但每次只允许移动一个盘,且在移动过程中在三个座上都始终保持大的在下,小的在上。算法:①先把n–1个盘子从a柱移到b柱(借助c柱);②把第n个盘子从a柱移到c柱;③把b柱上的n-1个盘子借助a柱移到c柱。4、例Hanoi塔问题古代有一个梵塔,塔内有void

move(charx,chary){printf(“%c%c\n”,x,y);}voidhanoi(intn,charone,chartwo,charthree){if(n==1)move(one,three);else{hanoi(n-1,one,three,two);move(one,three);hanoi(n-1,two,one,three);}}voidmove(charx,chary)main(){intm;printf(“inputthenumberofdiskes:”);scanf(“%d”,&m)printf(“Thesteptomoving%3ddiskes:\n”,m);hanoi(m,’A’,’B’,’C’);}运行结果:inputthenumberofdiskes:3Thesteptomoving3diskes:ACABCB ACBABCACmain()运行结果: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数组,并分别统计出两个数组相应元素大于、等于、小于的次数。程序如下:数组作为函数参数

1、数组元素作函数实参例有两个数组a、b,各有main(){intlarge(int,int);inta[10],b[10],i,n=0,m=0,k=0;printf(“enterarraya:\n”);for(i=0;i<10;i++)scanf(“%d”,&a[i]);printf(“\n”);printf(“enterarrayb:\n”);for(i=0;i<10;i++)scanf(“%d”,&b[i]);printf(“\n”);for(i=0;i<10;i++){if(large(a[i],b[i])==1)n=n+1;elseif(large(a[i],b[i])==0)m=m+1;elsek=k+1;}main()printf(“a[i]>b[i]%dtimes\na[i]=b[i]%dtimes\na[i]<b[i]%dtimes\n”,n,m,k);if(n>k)printf(“arrayaislargerthanarrayb\n”);elseif(n<k)printf(“arrayaissmallerthanarrayb\n”);elseprintf(“arrayaisequaltoarrayb\n”);}large(intx,inty){intflag;if(x>y)flag=1;elseif(x<y)flag=-1;elseflag=0;return(flag);}运行结果:Enterarraya:1,3,5,7,9,8,6,4,2,0Enterarrayb:5,3,8,9,–1,–3,5,6,0,4a[i]>b[i]4timesa[i]=b[i]1timesa[i]<b[i]5timesarrayaissmallerthanarraybprintf(“a[i]>b[i]%dtimes\na[ifloataverage(floatarray[10]){inti;floataver,sum=0;for(i=0;i<10;i++)sum=sum+array[i];aver=sum/10;return(aver);}2、数组名可作函数参数

例有一个一维数组score,内放10个学生成绩,求平均成绩。程序如下:floataverage(floatarray[10])main(){floatscore[10],aver;inti;printf(“intput10scores:\n”)for(i=0;i<10;i++)scanf(“%f”,&score[i]);printf(“\n”);aver=average(score);printf(“averagescoreis%5.2f”,aver);}运行结果:input10scores:100,56,78,98.5,76,87,99,67.5,75,97averagescoreis83.40main()运行结果:说明:(1)用数组名作函数参数,应该在主调函数和被调用函数分别定义数组。(2)实参数组与形参数组类型应一致(今都为float型),如不一致,结果将出错。(3)实参数组和形参数组大小可以一致也可以不一致,C编译对形参数组大小不做检查,只是将实参数组的首地址传给形参数组。说明:(1)用数组名作函数参数,应该在主调函数和被调用函数分(4)形参数组也可以不指定大小,在定义数组时在数组名后面跟一个空的方括弧,为了在被调用函数中处理数组元素的规模,可以另设一个参数,传递数组元素的个数。(5)用数组名作函数实参时,不是把数组的值传递给形参,而是把实参数组的起始地址传递给形参数组,这样两个数组就共占同一段内存单元。(4)形参数组也可以不指定大小,在定义数组时在数组名后面(5变量的作用域:局部变量与全局变量的应用

1、局部变量:在一个函数内部定义的变量是内部变量,只在本函数内部有效。2、全局变量:在函数外定义的变量称为外部变量,外部变量是全局变量,全局变量可以为本文件中其它函数共用,它的有效范围为从定义变量的位置开始到本源文件结束。

变量的作用域:局部变量与全局变量的应用1、局部变量:在一个例:变量的作用域。#include"stdio.h"intn=65;voidprint(){ printf("%d\n",n);}main(){ printf("%d\n",n); print();}

程序运行为6565我们可以看到,定义了全局变量,无论是主函数还是自定义的函数,都可以使用全局变量。

例:变量的作用域。我们可以看到,定义了全局变量,无论是主函数如果我们把程序改成这样:

#include"stdio.h"voidprint(){ printf("%d\n",n);}main(){ intn=65; printf("%d\n",n); print();}则在编译时,系统会提示在print()函数内的变量n未定义。其实在这个程序中,主函数中定义的变量n是一个局部变量,它的范围只是在主函数内,不能用在主函数之外的地方。如果我们把程序改成这样:#include"stdio.h例有一个一维数组,内放10个学生成绩,写一个函数,求出平均分、最高分和最低分。程序如下:例有一个一维数组,内放10个学生成绩,写一个函数,求程序如floatMax=0,Min=0;floataverage(floatarray[],intn){inti;floataver,sum=array[0];Max=Min=array[0];for(i=1;i<n;i++){if(array[i]>Max)Max=array[i];elseif(array[i]<Min)Min=array[i];sum=sum+array[i];}aver=sum/n;return(aver);}floatMax=0,Min=0;main(){floatave,score[10];inti;for(i=0;i<10;i++)scanf(“%f”,&score[i]);ave=average(score,10);printf(“max=%6.2f\nmin=%6.2f\naverage=%6.2f\n”,Max,Min,ave);}运行结果如下:9945789710067.589926643max=100.00min=43.00average=77.65main()运行结果如下:注意:

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

/max(inta,intb)/★a、b为局部变量★

/

{intc;c=a>b?a:b;return(c);}main(){inta=8;/★a为局部变量★

/

printf(“%d”,max(a,b));}形参a、b作用范围a、b作用范围局部变量a作用范围全局变量b的作用范围运行结果为:8例外部变量与局部变量同名。inta=3,b=5;静态存储方式:指在程序运行期间分配固定的存储空间的方式。动态存储方式:指在程序运行期间根据需要进行动态的分配存储空间的方式。用户使用的存储空间可以分为三部分,如右图所示:①程序区②静态存储区③动态存储区

程序区

静态存储区

动态存储区用户区

动态存储方式与静态存储静态存储方式:指在程序运行期间分配固定的存储空间的方式。用户

数据分别存放在静态存储区和动态存储区中。全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。程序执行过程中它们占据固定的存储单元,而不是动态地进行分配和释放。在动态存储区中存放以下数据:①函数形式参数。②自动变量(未加static声明的局部变量)③函数调用时的现场保护和返回地址等。数据分别存放在静态存储区和动态存储区中。全局局部静态变量1.局部静态变量(1)定义格式:static数据类型内部变量表;(2)存储特点3)何时使用局部静态变量1)需要保留函数上一次调用结束时的值。2)变量只被引用而不改变其值。1)静态内部变量属于静态存储。在程序执行过程中,即使所在函数调用结束也不释放。换句话说,在程序执行期间,静态内部变量始终存在,但其它函数是不能引用它们的。2)定义但不初始化,则自动赋以"0"(整型和实型)或'\0'(字符型);且每次调用它们所在的函数时,不再重新赋初值,只是保留上次调用结束时的值!局部静态变量1.局部静态变量3)何时使用局部静态变量1)静态例:打印1到5的阶乘值。intfac(intn){staticintf=1;f=f*n;return(f);}main(){inti;for(i=1;i<=5;i++)printf(“%d!=%d\n”,i,fac(i));}运行结果为:1!=12!=23!=64!=245!=120例:打印1到5的阶乘值。intfac(intn)运行结果(1)定义格式:[auto]数据类型变量表; (2)存储特点

1)自动变量属于动态存储方式。在函数中定义的自动变量,只在该函数内有效;函数被调用时分配存储空间,调用结束就释放。在复合语句中定义的自动变量,只在该复合语句中有效;退出复合语句后,也不能再使用,否则将引起错误。

2)定义而不初始化,则其值是不确定的。如果初始化,则赋初值操作是在调用时进行的,且每次调用都要重新赋一次初值。

3)由于自动变量的作用域和生存期,都局限于定义它的个体内(函数或复合语句),因此不同的个体中允许使用同名的变量而不会混淆。即使在函数内定义的自动变量,也可与该函数内部的复合语句中定义的自动变量同名。建议:系统不会混淆,并不意味着人也不会混淆,所以尽量少用同名自动变量!auto变量(1)定义格式:[auto]数据类型变量表; 建议:例:自动变量与静态局部变量的存储特性。voidauto_static(void){intvar_auto=0; /*自动变量:每次调用都重新初始化*/staticintvar_static=0; /*静态局部变量:只初始化1次*/printf(“var_auto=%d,var_static=%d\n”,var_auto,var_static);++var_auto;++var_static;}main(){inti;for(i=0;i<5;i++)auto_static();} 例:自动变量与静态局部变量的存储特性。

一般情况下,变量的值都是存储在内存中的。为提高执行效率,C语言允许将局部变量的值存放到寄存器中,这种变量就称为寄存器变量。定义格式如下:

register

数据类型变量表;(1)只有局部变量才能定义成寄存器变量,即全局变量不行。(2)对寄存器变量的实际处理,随系统而异。例如,微机上的MSC和TC将寄存器变量实际当作自动变量处理。(3)允许使用的寄存器数目是有限的,不能定义任意多个寄存器变量。寄存器存储──寄存器变量一般情况下,变量的值都是存储在内存中的。为提高执行效率,外部变量的存储方式外部变量属于静态存储方式:(1)静态外部变量──只允许被本源文件中的函数引用其定义格式为:static数据类型外部变量表;(2)非静态外部变量──允许被其它源文件中的函数引用定义时缺省static关键字的外部变量,即为非静态外部变量。其它源文件中的函数,引用非静态外部变量时,需要在引用函数所在的源文件中进行说明:extern数据类型外部变量表;注意:在函数内的extern变量说明,表示引用本源文件中的外部变量!而函数外(通常在文件开头)的extern变量说明,表示引用其它文件中的外部变量。外部变量的存储方式外部变量属于静态存储方式:(2)非静态外静态局部变量和静态外部变量同属静态存储方式,但两者区别较大:(1)定义的位置不同。静态局部变量在函数内定义,静态外部变量在函数外定义。(2)作用域不同。静态局部变量属于内部变量,其作用域仅限于定义它的函数内;虽然生存期为整个源程序,但其它函数是不能使用它的。静态外部变量在函数外定义,其作用域为定义它的源文件内;生存期为整个源程序,但其它源文件中的函数也是不能使用它的。(3)初始化处理不同。静态局部变量,仅在第1次调用它所在的函数时被初始化,当再次调用定义它的函数时,不再初始化,而是保留上1次调用结束时的值。而静态外部变量是在函数外定义的,不存在静态内部变量的“重复”初始化问题,其当前值由最近1次给它赋值的操作决定。静态局部变量和静态外部变量同属静态存储方式,但两者区别较大:务必牢记:把局部变量改变为静态内部变量后,改变了它的存储方式,即改变了它的生存期。把外部变量改变为静态外部变量后,改变了它的作用域,限制了它的使用范围。因此,关键字“static”在不同的地方所起的作用是不同的。务必牢记:把局部变量改变为静态内部变量后,改变了它的存储方式模块化程序设计方法

1、模块化程序设计方法的指导思想将一个大而复杂的设计任务按其需要实现的主要功能分解为若干相对独立的模块,将各模块的功能逐步细化为一系列的处理步骤或某种程序设计语言的语句。完成总任务的程序由一个主程序和若干个子程序组成,主程序起着任务调度的总控作用,而每个子程序各自完成一个单一的任务。然后分别编写,调试,最后再将它们的目标模块连接装配成一个完整的整体。

模块化程序设计的优点是:程序编制方便,易于修改和调试,可由多人分工合作完成,程序的可读性。可维护性及可扩充性强;子程序的代码公用,使程序简洁。模块化程序设计方法1、模块化程序设计方法的指导思想2、模块分解的原则(1)如果一个程序段被很多模块所公用,则它应是一个独立的模块。(2)如果若干个程序段处理的数据是公用的,则这些程序段应放在一个模块中。(3)若两个程序段的利用率差别很大,则应分属于两个模块。(4)一个模块既不能过大,也不能过小。过大则模块的通用性较差,过小则会造成时间和空间上的浪费。(5)力求使模块具有通用性,通用性越强的模块利用性越高。(6)各模块间应在功能上,逻辑上相互独立,尽量截然分开,特别应避免用转移语句在模块间转来转去。(7)各模块间的接口应该简单,要尽量减少公共变量的个数,尽量不用共用数据存储单元,在结构或编排上有联系的数据应放在一个模块中,以免相互影响,造成查错困难。(8)每个模块的结构应设计成单入口,单出口的形式。这样的程序便于调试,阅读和理解且可靠性高。2、模块分解的原则(1)如果一个程序段被很多模块所公用,则它1、内部函数

如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。即

static

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

内部函数又称静态函数。通常把只能由同一文件使用的函数和外部变量放在一个文件中,在它们前面都冠以static使之局部化,其他文件不能引用。内部函数和外部函数

1、内部函数如果一个函数只能被本文件中其他2、外部函数

(1)在定义函数时,如果在函数首部的最左端冠以关键字

extern,则表示此函数是外部函数,可供其他文件调用。如函数首部可以写为

externintfun(inta,intb)C语言规定,如果在定义函数时省略extern,则隐含为外部函数。(2)在需要调用此函数的文件中,用extern声明所用的函数是外部函数。2、外部函数(1)在定义函数时,如果在函数首部的最左端冠以课堂练习1、写一函数,统计字符串中字母的个数。要求用数组元素作为函数实参。提示:数组元素就是下标变量,它与普通变量并无区别。数组元素只能用作函数实参,其用法与普通变量完全相同:在发生函数调用时,把数组元素的值传送给形参,实现单向值传送。课堂练习1、写一函数,统计字符串中字母的个数。要求用数组元素intisalp(charc){if(c>='a'&&c<='z'||c>='A'&&c<='Z')return(1);elsereturn(0);}main(){inti,num=0; charstr[255]; printf("Inputastring:"); gets(str);for(i=0;str[i]!='\0';i++) if(isalp(str[i]))num++; puts(str); printf("num=%d\n",num);

} intisalp(charc)2、用直接递归调用计算整数和:1+2+3+4+…+n。答案:longsum(intn){if(n==1)return(1);

return(sum(n-1)+n);

}

2、用直接递归调用计算整数和:1+2+3+4+…+n。答案:3、输入长方体的长(l)、宽(w)、高(h),求长方体体积及正、侧、顶三个面的面积。提示:利用全局变量计算长方体的三个面的面积答案:3、输入长方体的长(l)、宽(w)、高(h),求长方体体积及ints1,s2,s3;intvs(inta,intb,intc){intv; v=a*b*c;s1=a*b;s2=b*c;s3=a*c; returnv;}main(){intv,l,w,h;clrscr();printf("\ninputlength,widthandheight:");scanf("%d%d%d",&l,&w,&h);v=vs(l,w,h);printf("v=%d,s1=%d,s2=%ds3=%d\n",v,s1,s2,s3);

} ints1,s2,s3;分析:要注意局部变量和全局变量的区别:局部变量是内部变量,在函数内定义,只在本函数内有效。另外对于局部变量要注意:main中定义的变量只在main中有效不同函数中同名变量,占不同内存单元形参属于局部变量可定义在复合语句中有效的变量局部变量可用存储类型:auto,register,static,(默认为auto)全局变量是外部变量,在函数外定义,可为本文件所有函数共用,有效范围为从定义变量的位置开始到本源文件结束,及有extern说明的其它源文件。分析:要注意局部变量和全局变量的区别:7.1函数7.2函数的定义7.3函数的参数和返回值7.4函数的调用7.5函数的嵌套与递归调用7.6数组与函数参数7.7指针与函数7.8变量的存储类别7.9模块化程序设计方法补充:如何运行一个多文件的程序

第七章函数与模块化程序设计方法7.1函数第七章函数与模块化程序设计方法

一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能.一个C程序可由一个主函数和若干个函数构成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。下图是一个程序中函数调用的示意图:maingedefghhicba函数概述

一个较大的程序一般应分为若干个程序模块,每一main()/*主函数*/{

print_star();/*调用print_star函数画****/

print_message();/*调用print_message函数写字*/

print_star();/*调用print_star函数画****/}print_star()/*定义print_star函数*/{

prinf(“\n**********”);}print_message()/*定义print_message函数*/

{prinf(“\nHello!”);}函数调用实例:运行结果:**********

Hello!**********

main()说明:(1)一个源程序文件由一个或多个函数组成。一个源程序文件是一个编译单位,即以源程序为单位进行编译,而不是以函数为单位进行编译。(2)一个C程序由一个或多个源程序文件组成。一个源文件可以为多个C程序公用。(3)C程序的执行从main函数开始,调用其他函数后流程回到

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

{声明部分语句

}2、有参函数定义的一般形式

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

{声明部分语句

}

函数的定义1、无参函数的定义形式类型标识符函数名()2、有参函数定例如:

intmax(intx,inty){intz;z=x>y?x:y;return(z);}例如:3、可以有“空函数”它的形式为:

类型说明符函数名()

{}例如:

dumy(){}3、可以有“空函数”它的形式为:例如:4、对形参的声明的传统方式

在老版本C语言中,对形参类型的声明是放在函数定义的第2行,也就是不在第l行的括号内指定形参的类型,而在括号外单独指定,例如:intmax(x,y)intx,y;{intz;z=x>y?x:y;return(z);}

一般把这种方法称为传统的对形参的声明方式,而把前面介绍过的方法称为现代的方式。TurboC和目前使用的多数C版本对这两种方法都允许使用,两种用法等价。4、对形参的声明的传统方式在老版本C语言中,对形1、形式参数和实际参数

在定义函数时函数名后面括弧中的变量名称为“形式参数”,在主调函数中调用一个函数时,函数名后面括弧中的参数(可以是一个表达式)称为“实际参数”。函数的参数和返回值

1、形式参数和实际参数在定义函数时函数名后面例:调用函数时的数据传递main(){inta,b,c;scanf(“%d%d”,&a,&b);c=max(a,b);printf(“Maxis%d”,c);}intmax(intx,inty){intz;z=x>y?x:y;return(z);}运行情况:

7,8Maxis8例:调用函数时的数据传递main()运行情况:(1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数

max中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。(2)实参可以是常量、变量或表达式,如:

max(3,a+b);

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

语句将被调用函数中的一个确定值带回主调函数中去。

return

语句后面的括弧也可以不要,如:

returnz;return后面的值可以是一个表达式。

int

max(intx,inty){return(x>y?x:y);}函数的返回值(1)函数的返回值是通过函数中的return语句(2)函数值的类型。在定义函数时指定函数值的类型。例如:intmax(floatx,floaty)

/★函数值为整型★/charletter(charc1,charc2)

/★函数值为字符型★/doublemin(intx,inty)

/★函数值为双精度型★/(2)函数值的类型。在定义函数时指定函数值的类型。例如:in

C语言规定,凡不加类型说明的函数,一律自动按整型处理。在定义函数时对函数值说明的类型一般应该和return语句中的表达式类型一致。例返回值类型与函数类型不同。main(){floata,b;intc;scanf(“%f,%f,”,&a,&b);c=max(a,b);printf(“”Maxis%d\n”,c);}

max(floatx,floaty);{floatz;/★z为实型变量★/z=x>y?x:y;return(z);}运行情况如下:1.5,2.5Maxis2C语言规定,凡不加类型说明的函数,一律自动按(3)如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。(4)如果被调用函数中没有return语句,并不带回一个确定的、用户所希望得到的函数值,但实际上,函数并不是不带回值,而只是不带回有用的值。带回的是一个不确定的值。(5)为了明确表示“不带回值”,可以用“void”定义“无类型”(或称“空类型”)(3)如果函数值的类型和return语句中表达式的值不一致,1、函数调用的一般形式

函数名(实参表列);

如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略,如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应一致。实参与形参按顺序对应,一一传递数据。如果实参表列包括多个实参,对实参求值的顺序并不是确定的,有的系统按自左至右顺序求实参的值,有的系统则按自右至左顺序。

许多

C版本(例如

Turbo

C和

MSC)是按自右而左的顺序求值。

函数的调用

1、函数调用的一般形式函数名(实参表列);2、函数调用的方式

按函数在程序中出现的位置来分,可以有以下三种函数调用方式:(1)函数语句

把函数调用作为一个语句。如:

printstar();2、函数调用的方式按函数在程序中出现的位置来分(2)函数表达式

函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参加表达式的运算。例如:

c=2*max(a,b);(3)函数参数函数调用作为一个函数的实参。例如:m=max(a,max(b,c));(2)函数表达式函数出现在一个表达式中,这种3、对被调用函数的声明和函数原型

在一个函数中调用另一函数(即被调用函数)需要具备的条件(1)首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。(2)如果使用库函数,一般还应该在本文件开头用#include命令将调用有关库函数时所需用到的信息“包含”到本文件中来。例如,前几章中已经用过的

#include<stdio.h>(3)如果使用用户自己定义的函数,而且该函数与调用它的函数(即主调函数)在同一个文件中,一般还应该在主调函数中对被调用的函数作声明,即对编译系统声明将要调用此函数,并将有关信息通知编译系统。3、对被调用函数的声明和函数原型在一个函数中调用另一函数(例对被调用的函数作声明main(){floatadd(floatx,floaty);/★对被调用函数的声明★

/floata,b,c;scanf(“%f,%f”,&a,&b);c=add(a,b);printf(“sumis%f”,c);}floatadd(floatx,floaty);/

★函数首部★

/{floatz;/

★函数体★

/z=x+y;return(z);}运行情况如下:3.5,6.5sumis10.000000例对被调用的函数作声明main()运行情况如下:在函数声明中也可以不写形参名,而只写形参的类型。如:

floatadd(float,float);在C语言中,以上的函数声明称为函数原型(functionprototype)。它的作用主要是利用它在程序的编译阶段对调用函数的合法性进行全面检查。函数原型的一般形式为①函数类型函数名(参数类型1,参数类型2……)②函数类型函数名(参数类型1参数名1,参数类型2

参数名2,……)

第①种形式是基本的形式。为了便于阅读程序,也允许在函数原型中加上参数名,就成了第②种形式。但编译系统不检查参数名。因此参数名是什么都无所谓。在函数声明中也可以不写形参名,而只写形参的类型。如:在C语言说明:①

如果在函数调用之前,没有对函数作声明,则编译系统会把第一次遇到的该函数形式(函数定义或函数调用)作为函数的声明,并将函数类型默认为int型。

如果函数类型为整型,可以在函数调用前不必作函数声明。但是使用这种方法时,系统无法对参数的类型做检查。若调用函数时参数使用不当,在编译时也不会报错。因此,为了程序清晰和安全,建议都加以声明为好。②

如果被调用函数的定义出现在主调函数之前,可以不必加以声明。说明:①如果在函数调用之前,没有对函数作声明,则编译系统会③

如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必对所调用的函数再作声明。例如:charletter(char,char);/★以下3行在所有函数之前,且在函数外部★

/floatf(float,float);inti(float,float);main(){…}/

★不必声明它所调用的函数★

/charletter(charc1,charc2)/

★定义letter函数★

/{…}floatf(floatx,floaty)/

★定义f函数★

/{…}inti(floatj,floatk)/

★定义i函数★

/{…}③如果已在所有函数定义之前,在函数的外部已做了函数声cha例题:写出下列程序的输出结果

(1)#include<stdio.h>swap(inta,intb){inttemp;temp=a;a=b;b=temp;}main(){intx=7,y=11;printf("x=%d,\ty=%d\n",x,y);printf("swapped:\n");swap(x,y);printf("x=%d,\ty=%d\n",x,y);}答案:x=7 y=11swapped:

温馨提示

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

评论

0/150

提交评论