版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章函数6.1函数基础6.2函数调用6.3函数的嵌套调用和递归调用6.4变量的作用域和存储类别6.5外部函数与内部函数6.6应用举例6.1函数基础6.1.1概述6.1.2函数定义6.1.3函数的参数6.1.4函数的返回值6.1.1概述函数是C源程序的基本模块,通过对函数模块的调用实现特定的功能。C语言不仅提供了极为丰富的库函数(如TurboC、MSC都提供了三百多个库函数),还允许用户建立自己定义的函数。可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函数式语言。6.1.1概述在C语言中,可从不同的角度对函数分类。(1)从函数定义的角度看,函数可分为库函数和用户定义函数两种。(2)C语言的函数兼有其他语言中的函数(有返回值函数)和过程(无返回值函数)两种功能。(3)从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。6.1.1概述(4)库函数又可从功能角度作以下分类。字符类型分类函数转换函数目录路径函数诊断函数图形函数输入输出函数
接口函数
字符串函数
内存管理函数
数学函数
日期和时间函数
进程控制函数
其他函数
6.1.1概述在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。函数还可以自己调用自己,称为递归调用。main函数是主函数,它可以调用其他函数,而不允许被其他函数调用。一个C源程序必须有,也只能有一个主函数main。6.1.2函数定义1.无参函数的定义形式
类型标识符
函数名()
{
声明部分
语句
}
其中类型标识符和函数名称为函数头。{}中的内容称为函数体。在很多情况下都不要求无参函数有返回值,此时函数类型符可以写为void。6.1.2函数定义2.有参函数定义的一般形式
类型标识符
函数名(形式参数列表)
{
声明部分
语句
}
有参函数比无参函数多了一个内容,即形式参数列表。形参是变量,必须在形参表中给出形参的类型说明。6.1.2函数定义【例6-1】intmin(inta,intb)
/*定义函数min*/{if(a<b)returna;/*比较a,b,返回较小值*/
elsereturnb;}main(){intmin(inta,intb);/*min函数进行说明*/
intx,y,z;printf("inputtwonumbers:\n");scanf("%d%d",&x,&y);z=min(x,y); /*调用函数min*/printf("minmum=%d",z);}6.1.2函数定义【例6-1】程序运行结果如下图所示。6.1.3函数的参数函数的参数分为形参和实参两种。形参出现在函数定义中,实参出现在主调函数中,形参和实参的功能是作数据传送。函数的形参和实参具有以下特点:(1)形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。(2)在进行函数调用时,应预先用赋值、输入等办法使实参获得确定值。(3)实参和形参在数量上、类型上、顺序上应严格一致,否则会发生类型不匹配的错误。(4)函数调用中发生的数据传送是单向的。6.1.3函数的参数【例6-2】下面的程序可以说明这个问题。main(){intn;printf("inputnumber\n");scanf("%d",&n);s(n); /*调用函数s*/printf("ShiCann=%d\n",n);/*输出实参n的值*/}ints(intn) /*定义函数s*/6.1.3函数的参数{
inti;for(i=n-1;i>=1;i--)n=n+i;printf(“XingCann=%d\n”,n);/*输出形参n的值*/}【例6-2】程序运行结果如下图所示。6.1.4函数的返回值函数的值(或称函数返回值)是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的值。对函数的值有以下一些说明:(1)函数的值只能通过return语句返回主调函数。
return语句的一般形式是:
return表达式;
或者为:
return(表达式);
(6.1.4函数的返回值(2)函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类型为准,自动进行类型转换。(3)如函数值为整型,在函数定义时可以省去类型说明。(4)不返回函数值的函数,可以明确定义为“空类型”,类型说明符为“void”。形式为:
voids(intn) {…… }
一旦函数被定义为空类型后,就不能在主调函数中使用被调函数的函数值了。6.2函数调用6.2.1函数声明6.2.2函数调用方式6.2.3函数调用中的值传递方式6.2.1函数声明在主调函数中调用某函数之前应对该被调函数进行声明,目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。对被调函数的声明也有两种格式:传统格式,其一般格式为:
类型说明符
被调函数名();现代格式,其一般形式为:
类型说明符
被调函数名(类型
形参,类型
形参…);或为:
类型说明符
被调函数名(类型,类型…);6.2.1函数声明C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数声明,直接调用。(1)如果被调函数的返回值是整型或字符型时。这时系统将自动对被调函数返回值按整型处理。(2)当被调函数的函数定义出现在主调函数之前时。(3)如在所有函数定义之前,在函数外预先说明了各个函数的类型。(4)对库函数的调用不需要再作声明,但必须把该函数的头文件用include命令包含在源文件前部。6.2.1函数声明【例6-3】floatf(floatb); /*对f函数预先作了声明*/main(){floatn,m;n=0.1;m=f(n); /*直接调用函数f*/printf("%f\n",f(m));}floatf(floatb){returnb*b;}6.2.1函数声明【例6-3】程序运行结果如下图所示。6.2.2函数调用方式C语言中,函数调用的一般形式为:
函数名(实参表)可以用以下几种方式调用函数: (1)函数表达式
(2)函数语句 (3)函数实参6.2.2函数调用方式【例6-4】voidfun(); /*对函数进行说明*/main(){intcount;for(count=1;count<=3;count++)fun(); /*
循环调用fun函数*/}voidfun()
/*定义fun函数*/{
inti=1;i+=2;printf("%d",i);}6.2.2函数调用方式【例6-4】程序运行结果如下图所示。6.2.3函数调用中的值传递方式实参与形参按顺序对应,一一传递数据。所以,这种函数调用又称传值调用。实参变量对形参变量的数据传递为“值传递”,即单向传递,只能由实参传给形参,而不能由形参传回给实参。在内存中,形参与实参各自有不同的存储单元。6.2.3函数调用中的值传递方式【例6-5】intswap(x,y) /*定义函数swap*/{intt;t=x;x=y;y=t; /*交换x,y的值*/
printf("x=%d,y=%d\n",x,y);}main(){inta,b;scanf("%d,%d",&a,&b);if(a<b)swap(a,b); /*调用函数swap*/printf("a=%d,b=%d\n",a,b);}6.2.3函数调用中的值传递方式设从键盘输入的值为15和19,即a=15,b=19。在执行swap(a,b)函数时,系统自动为形参x、y分配内存单元。如下图所示。6.2.3函数调用中的值传递方式【例6-5】程序运行结果如下图所示。6.3函数的嵌套调用和递归调用6.3.1函数嵌套调用6.3.2函数递归调用6.3.1函数嵌套调用函数的嵌套调用,即在被调函数中又调用其他函数。其关系可表示如下图所示。6.3.1函数嵌套调用【例6-6】计算s=22!+32!
本例可编写两个函数,一个是用来计算平方值的函数f1,另一个是用来计算阶乘值的函数f2。longf1(intp) /*定义函数f1, 计算平方值*/{
intk;longr;longf2(int);
/*声明函数f2*/6.3.1函数嵌套调用
k=p*p;r=f2(k); /*调用函数f2*/returnr;}longf2(intq) /*定义函数f2, 计算阶乘值*/{
longc=1;inti;for(i=1;i<=q;i++)c=c*i;returnc;}6.3.1函数嵌套调用main(){inti;longs=0;for(i=2;i<=3;i++)s=s+f1(i); /*调用函数f1*/printf("\ns=%ld\n",s);}【例6-6】程序运行结果如下图所示。6.3.2函数递归调用一个函数在它的函数体内调用它自身称为递归调用。这种函数称为递归函数。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。6.3.2函数递归调用【例6-8】#include"stdio.h"try() /*定义函数try*/{charc;if((c=getchar())!=’\n’)try(); /*递归调用try, 直到输入回车*/
putchar(c); /*从栈中输出字符*/}main(){try();/*调用try函数*/}6.3.2函数递归调用假设输入的内容为computer↙,从函数try()可以看出,当输入为换行符时为递归的终止条件,结束递归调用。设第i次调用的局部字符变量记为ci,局部变量在栈中的存储情况如下图所示。6.3.2函数递归调用【例6-8】程序运行结果如下图所示。6.4变量的作用域和存储类别6.4.1变量的作用域6.4.2变量的存储类别6.4.1变量的作用域1.局部变量局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内。关于局部变量的作用域还要说明以下几点:(1)主函数中定义的变量也只能在主函数中使用,同时,主函数中也不能使用其他函数中定义的变量。(2)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。(3)允许在不同的函数中使用相同的变量名,它们代表不同的对象。(4)在复合语句中也可定义变量,其作用域只在复合语句范围内。6.4.1变量的作用域【例6-9】main(){inti=2,j=3,k;/*在main中定义了i,j,k三个变量*/
k=i+j;{intk=8; /*在复合语句内又定义变量k*/printf("%d\n",k);/*输出复合语句内的k值*/}
printf("%d\n",k);
/*输出main中的k值*/}6.4.1变量的作用域【例6-9】程序运行结果如下图所示。6.4.1变量的作用域2.全局变量全局变量也称为外部变量,它是在函数外部定义的变量。其作用域是整个源程序。全局变量的说明符为extern。【例6-10】输入正方体的长宽高l、w、h。求体积及三个面x*y、x*z、y*z的面积。ints1,s2,s3; /*定义全局变量*/intvs(inta,intb,intc) /*函数vs*/{intv; /*定义局部变量v*/6.4.1变量的作用域
v=a*b*c;s1=a*b;s2=b*c;s3=a*c;returnv;}main(){intv,l,w,h;
/*定义main函数中的变量*/
printf("\ninputlength,widthandheight\n");scanf("%d%d%d",&l,&w,&h);v=vs(l,w,h); /*调用函数vs*/6.4.1变量的作用域
printf("\nv=%d,s1=%d,s2=%d,s3=%d\n",v,s1,s2,s3);
/*输出全局变量的值*/}【例6-10】程序运行结果如下图所示。6.4.1变量的作用域【例6-11】外部变量与局部变量同名。inta=3,b=5; /*a,b为外部变量*/max(inta,intb) /*a,b为外部变量*/{intc;c=a>b?a:b;return(c);}main(){inta=8;printf("%d\n",max(a,b));}6.4.1变量的作用域【例6-11】程序运行结果如下图所示。如果同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。6.4.2变量的存储类别1.动态存储方式与静态存储方式静态存储方式:是指在程序运行期间分配固定的存储空间的方式。动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。用户存储空间可以分为三个部分,结构如下图所示。6.4.2变量的存储类别全局变量全部存放在静态存储区。动态存储区存放以下数据:(1)函数形式参数。(2)自动变量(未加static声明的局部变量)。(3)函数调用实参的现场保护和返回地址。6.4.2变量的存储类别2.自动变量函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。3.寄存器变量将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。6.4.2变量的存储类别【例6-12】计算阶乘。intfac(intn){registerinti,f=1; /*定义寄存器变量*/
for(i=1;i<=n;i++)f=f*i; /*计算阶乘*/
return(f);}main(){inti;6.4.2变量的存储类别
for(i=0;i<=5;i++)printf(“%d!=%d\n”,i,fac(i)); /*计算0——5的阶 乘*/}【例6-12】程序运行结果如下图所示。6.4.2变量的存储类别【例6-13】计算π的值。doublepai(doubleeps)/*定义函数pai*/{registerdoubles=0.0,t=1.0;/*定义寄存器变量*/
intn;for(n=1;t>eps;n++){s+=t;t=n*t/(2*n+1);}return(2.0*s);}6.4.2变量的存储类别main(){doublepi;pi=pai(0.00000001);
/*调用函数pai*/printf("pai=%f\n",pi);}【例6-13】程序运行结果如下图所示。6.4.2变量的存储类别说明:①只有局部自动变量和形式参数可以作为寄存器变量。②一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量。③局部静态变量不能定义为寄存器变量。④不同的系统对寄存器变量的处理是不同的。6.4.2变量的存储类别4.外部变量外部变量(即全局变量)是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。在引用之前用关键字extern对该变量作“外部变量声明”。6.4.2变量的存储类别【例6-14】用extern声明外部变量,扩展程序文件中的作用域。intmax(intx,inty) /*定义函数max, 返回x,y中较大值*/{intz;z=x>y?x:y;return(z);}main(){externA,B; /*声明外部变量A,B*/printf("%d\n",max(A,B));}6.4.2变量的存储类别【例6-14】程序运行结果如下图所示。6.4.2变量的存储类别【例6-15】给定b的值,输入a和m,求b+a*m的值。文件p1.c的内容如下:inta;main(){intp();intb=3,c,m;print("inputaandm:\n");scanf("%d,%d",&a,&m);c=p(b,m); /*调用函数p*/printf("c=%d\n",c);}6.4.2变量的存储类别文件p2.c的内容如下:externinta;
/*定义外部变a,从p1.c中获得值*/intp(intx,inty){intz;z=x+a*y;returnz;}【例6-15】程序运行结果如下图所示。6.4.2变量的存储类别5.静态变量如果希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为“静态局部变量”,用关键字static进行声明。6.4.2变量的存储类别对静态局部变量的说明:(1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。(2)静态局部变量在编译时赋初值,即只赋初值一次。(3)如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。6.4.2变量的存储类别【例6-17】打印1到5的阶乘值。intfac(intn){staticintf=1; /*定义静态变量f*/f=f*n; /*改变f值*/
return(f);}main(){inti;for(i=1;i<=5;i++)printf("%d!=%d\n",i,fac(i)); /*循环调用fac*/}6.4.2变量的存储类别【例6-17】程序运行结果如下图所示。6.5外部函数与内部函数6.5.1外部函数6.5.2内部函数6.5.1外部函数能够被其他文件的函数调用的函数称为外部函数。 其定义形式为:
extern类型标识符
函数名(形参表)例如:
externfloatpo(a,b,c)这时,函数po可以被其他文件调用,如果函数定义时省略extern,则隐含为外部函数。6.5.2内部函数所谓内部函数,就是只能被本文件中其他函数调用的函数。 它的一般形式为:
static类型标识符
函数名(形参表)例如:
staticintpo(a,b,c)
内部函数又称为静态函数,使用内部函数,可以使函数只局限于所在文件,如果在不同文件中有同名的内部函数,将互不干扰。6.6应用举例【例6-20】编写一函数(remember)使其具有记录本身被调用次数的功能。编程思想:在函数内部声明的变量一般为auto型变量(一般auto可省略),它是局部变量只在函数内部有效,当在函数被调用时,系统才为它分配内存,当函数调用完毕,系统将变量释放内存。6.6应用举例源程序:#include"stdio.h"intremember(){staticintn=0; /*定义静态变量n*/n++; /*记录函数调用次数*/
returnn; /*返回函数调用次数*/}intmain(){ /*调用remeber函数并打印函数调用次数*/6.6应用举例printf("thefunctionhasbeencalled%dtimes",remember());
printf("thefunctionhasbeencalled%dtimes",remember());printf("thefunctionhasbeencalled%dtimes",remember());printf("thefunctionhasbeencalled%dtimes",remember());return1; }6.6应用举例【例6-20】程序运行结果如下图所示。6.6应用举例【例6-21】汉诺塔(Hanoi)问题。汉诺塔(Hanoi)问题是一个著名的问题,初始模式如下页图示。据说其来源于在约19世纪末欧洲的商店中出售的一种智力玩具。在一块铜板上有三根杆,最左边的杆上自上而下、自小到大顺序串着由64个圆盘构成的塔,游戏的目的是将最左边A杆上的圆盘,借助最右边的C杆,全部移到中间的B杆上,条件是依次仅能移动一个盘,且不允许大盘放在小盘的上边。6.6应用举例编程思路:
这是一个典型的非数值问题,如果使用非递归算法求解此问题,要设计复杂的算法,编写很长的程序。而使用递归算法可以使整个算法清晰,使程序简洁。本问题分析并建立递归算法的过程是典型的抽取问题本质、建立递归算法并最终解决非数值问题递归实例。6.6应用举例源程序:inti=0;voidmovedisc(unsignedintn,charfromneedle,chartoneedle,charusingneedle){if(n==1)
printf("%2d-(%2d):%c==>%c\n",++i,n,fromneedle,toneedle);
/*将fromneedle上的一个圆盘移到toneedle*/else6.6应用举例
{
movedisc(n-1,fromneedle,usingneedle,toneedle);/*将fromneedle上的n-1个圆盘借助toneedle 移到usingneedle*/printf("%2d-(%2d):%c==>%c\n",++i,n,fromneedle,toneedle);/*将fromneedle上的n号圆盘移到toneedle*/movedisc(n-1,usingneedle,toneedle,fromneedle);/*将刚才移到usingneedle上的n-1个圆盘借助 fromneedle移到toneedle*/}}6.6应用举例intmain(intargc,char*argv[]){unsignedn;printf("Pleaseenterthenumberofdiscs:"); /*输入n值*/
scanf("%d",&n);movedisc(n,‘a’,‘b’,‘c’);/*将A上的 N个圆盘 借助C移动到B上*/
printf("\tTotal:%d\n",i);/*暂停*/
charc=getchar();if(c!='')getchar();return0;}6.6应用举例【例6-21】程序运行结果如下图所示。6.6应用举例【例6-22】利用函数的递归解开跳马问题。跳马问题是一种经典的递归算法实现问题,如迷宫问题-找出所有能走出迷宫的路径,都使用“试探”性的递归算法,这种算法在实际生活中有着广泛的应用,如:在人工智能和自动控制领域。
以下使用这个实例:在5*5的棋盘(国际象棋)上,如下页左图和下页右图所示,从坐标(0,0)出发,按日字跳马,要求不重复的跳经所有位置,求出符合规则的所有跳马方案。6.6应用举例6.6应用举例编程思路:程序本身采用的是递归算法,通过棋子的当前位置,寻找下一步可能的位置。之所以采用递归算法,是因为在求解跳马方案过程中,是采用“试探”的方式,因而必然存在回退问题,递归调用能非常方便的解决回退问题,但在返回前应清除或还原沿途所作的状态标记和记录的位置坐标数据。6.6应用举例什么是试探的方法呢?如下图所示。6.6应用举例程序流程图如右图所示6.6应用举例源程序:inta[5][5],b[5],c[5],MAX=5,scheme=0; /*定义5*5 的棋盘*/ /*跳马函数,通过递归调用,寻找下一步的位置*/inthorse(intx,inty,intstep){inti,j;/*标识棋子在棋盘中的当前位置已跳过*/
a[x][y]=1; /*记录棋子在棋盘中的当前位置*/
b[step]=x;c[step]=y;/*判断是否找到符合规则的完整的跳马方案(跳马步数为24步时)*/
if(step>=MAX*MAX-1)print();6.6应用举例
/*打印完整的跳马路径*//*寻找下一步位置,共有八种可选位置,逐一进行试探,如果符合要求(此位置不越界,在棋盘内,且此点未被跳过),则继续递归调用寻找此位置的下一位置,如此点不符合要求,则返回0,递归回退。*/if((i=x+1)>=0&&(i=x+1)<MAX&&(j=y+2)>=0&&(j=y
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年纺织企业间产品买卖合同
- 2024年电动车零部件制造与技术许可合同3篇
- 2024简易工程装修合同
- 2025年度环保设施维护与升级补充合同模板3篇
- 专业化海运出口物流合作合同(2024年版)版
- 2024桩基破桩头作业服务协议版B版
- 2024年旅游业务合作合同详细条款
- 2024年水资源开发与利用合作协议
- 2024皮草产品定制加工及销售合作协议3篇
- 2024青岛装修工程纠纷解决合同范本3篇
- 大使涂料(安徽)有限公司年产6万吨科技型工业涂料、水性环保涂料生产项目环境影响报告书
- 利乐包和康美包的比较
- 法院执行庭长供职报告1400字
- 推动架机械加工工序卡片
- 重庆市綦江区篆塘镇白坪村建筑用砂岩矿采矿权评估报告
- 甘肃社火100首歌词
- 行政查房情况记录表
- GB/T 2315-2000电力金具标称破坏载荷系列及连接型式尺寸
- 腹主动脉瘤的护理查房
- 星级酒店每日防火巡查记录本
- 中石化erp系统操作手册
评论
0/150
提交评论