函数对C++的作用_第1页
函数对C++的作用_第2页
函数对C++的作用_第3页
函数对C++的作用_第4页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

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

文档简介

函数是C和C++程序的基本模块,是构成结构化程序的基本单元。在前面的章节中,我们虽然有了函数的初步概念,但那仅是感性的认识。况且,设计一个复杂的C程序,通常不仅包含有多个函数,还可能包含多个源文件。于是,我们就面临一些问题:程序中函数要怎样设计定义,如何调用,函数参数如何传递等。另外,在程序中如何访问各函数和各文件中定义的变量,变量引用时的作用域规则,数据生存期也是我们要堇点解决的问题。本章将根据以上问题,以多个实例为引导,循序渐进地讨论函数定义、调用,参数传递,嵌套及递归调用,局部变量和全局变量,变量存储方式,内部函数和外部函数等相关知识。通过本章的学习,将能够使读者掌握C语言函数的操作应用。函数定义与调用在结构化程序设计中,一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能。而模块一般是通过子程序来实现的。在TurboC中,子程序的作用是山函数完成的。函数是一个自我包含地完成一定相关功能的执行代码段。我们可以把函数看成一个“黑盒子”,你只要将数据送进去就能得到结果,而函数内部究竟是如何工作的,外部程序是不知道的。外部程序所知道的仅限于输入给函数什么以及函数输出什么。先看实例。【例5.1】简单的C函数定义与调用程序定义输出一串字符函数:printMessage() /*定义printMessage()函数*/(printf("Hello,world!\nu);/*在屏幕上输出字符串Hello,world!*/

定义在一行打印n个的函数:printStar(intn) /*定义printStar()函数*/{intent;for(cnt=0;cnt<n;cnt++)printf("**');printf(H\nH);在主程序中调用它们:include<stdio.h>/*把输入输出函数的头文件包含进来*/main()intcount;printf("Pleaseintputstarnumbers(count):,1);scanf("%d",&count);printStar(count); /*调用printStar()函数*/printMessage(); /*调用printMessage()函数*/printStar(count); /*留用printStar()函数*/return0; /*退出main()函数,并返回0*/其中,函数printStar()、printMessage。都是用户自定义的函数。在TurboC环境卜编辑好上述程序进行编译后,如果无任何错误,就可以按下Ctrl+F9运行程序。例5.1的运行结果如下:(代表回车,下同)Pleaseinputstarnumbers(count):10/Hello,world!我们发现,程序的运行结果一闪而过,按下Alt+F5后,才能看到程序的执行结果,并且,当多次执行上述程序时,发现前面执行的结果仍显示出来。对C的用户来讲,这样的显示方式并不直观。让我们来对这个程序稍加改进,我们只要将上述程序中的main()函数改为以下形式:main()intcount;clrscr();scanf(M%dM,&count);printStar(count);scanf(M%dM,&count);printStar(count);printMessage();printStar(count);getch();return0;/*调用printStar()函数*//*调用printMessage()函数*//*退出main()函数,并返回0/*退出main()函数,并返回0*/}然后在程序的开头部分加上语句:#include<conio.h>o因为函数clrscr()及getch()是C的标准库函数,它们都在头文件conio.h中定义说明。其中函数clrscr()的作用是程序执行时先清屏;函数getch()的作用等待从键盘上接收一个字符,但该字符并不显示。函数定义1.函数声明形式在C中,从用户使用角度把函数分为标准库函数(如printf()>scanf()、clrscr()>getch()等)和用户自定义函数(如例5.1中的printStar(n)、printMessage());而从函数的定义形式上把函数分为无参函数和有参函数。首先我们看一个简单的例子。【例5.2】有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13,…。设计一个函数求该数列前20项的和。项数从主程序输入。设计步骤如下:#include<stdio.h>#include<conio.h>floatindex(intn); /*函数原型声明*/main()intnumber;clrscr();printf("Pleaseinputelementnumbers(number):");scanf("%d”,&number);printf(MThesumis%9.6f\n,\index(number));getch();floatindex(intnum)/*函数定义*/intent;floattemp,a=2,b=1,s=0;for(cnt=l;cnt<=num;cnt++)s=s+a/b;temp=a;a=a+b;b=temp; /*这部分是程序的关键,请读者猜猜temp的作用*/retum(s);程序输出是:Pleaseinputelementnumbers(number):20/Thesumis32.660259由例5.2,我们可以看到,在程序的头部有一条语句:floatindex(intn);它的作用是什么呢?我们看看下面的解释。TurboC中所有函数与变量一样,在使用之前需要声明(称为函数原型FunctionPrototype)»所谓声明是指声明函数是什么类型的函数,一般库函数的声明都包含在相应的头文件v*.h>中,例如标准输入输出函数包含在stdio.h中,非标准输入输出函数包含在io.h中,以后在使用库函数时必须先知道该函数包含在什么样的头文件中,在程序的开头用includev*.h>或#include"*.h"声明。只有这样程序在编译、连接时TurboC才知道它是提供的库函数,否则,将认为是用户自己编写的函数而不能装配。函数声明的ANSI规定方式如下:[extern]函数类型函数名([形式参数表D;其中:函数类型是该函数返回值的数据类型,可以是以前介绍的整型(int)、长整型(long)、字符型(char)、单浮点型(float)、双浮点型(double)以及无值型(void)、也可以是指针、包括结构指针。无值型表示函数没有返回值。方括号“口”代表可选。函数名为TurboC的标识符,括号中的内容为该函数的形式参数说明。可以只有数据类型而没有形式参数,也可以两者都有。对于经典的函数说明没有参数信息。如:iniputexp(intx,z,intcolor,char*p);/*声明一个整型函数*/char*name(void); /*声明一个字符串指针函数*/voidstudent(intn,char*str);/*声明一个不返I可值的函数*/floatcalculate(int,int); /*声明一个浮点型函数*/值得注意的是:如果一个函数没有声明就被调用,编译程序并不认为出错,而将此函数默认为整型(int)函数。因此当一个函数返回其它类型,又没有事先声明,编译时将会出错。2.函数定义形式函数定义就是确定该函数完成什么功能以及怎么运行,相当于其它语言的一个子程序。TurboC对函数的定义采用ANSI规定的方式。即:[extern/static]函数类型函数名([形式参数表])函数体;其中:函数类型和形式参数的数据类型为TurboC的基本数据类型。函数体为TurboC提供的库函数和语句以及其它用户自定义函数调用语句的组合,并包括在一对花括号“{”和“}”中。若函数名后面的括号内无形式参数,则称该函数是无参函数(函数名后括号不能省略),否则称是有参函数。需要指出的是:一个程序必须有一个主函数main。,其它用户定义的子函数可以任意多个,这些函数的位置也没有什么限制,可以在main()函数前,也可以在其后。【例5.3】无参函数的应用实例。#include<stdio.h>voidhello_world(void)printf("Hello,world!\n");voidthree_hellos(void)intcounter;for(counter=1;counter<=3;counter++)hello_world(); /*调用hello_world()函数*/voidmain(void)three_hellos(); /*调用函数three_hellos()*/程序输出।是:

Hello,world!Hello,world!Hello,world!提示:当函数无参数时,在形参表中可使用关键字void。尽管这不是必须的,但我们建议读者这样使用。另外,当函数在主函数main()之前定义时,则不需函数声明。【例5.4]定义一个函数,求三个数中最大者。要求从键盘输入三个数,调用该函数,输出最大数。#include<conio.h>intmaxmum(int,int9int); /*用户自定义函数声明*/main(){intnuml,num2,num3,max;clrscr();printf(nPleaseinputthreenumbers(numlandnum2andnum3):");scanf(M%d%d%d",&numl,&num2,&num3);max=maxmum(numl,num2,num3);/*调用子函数,并将返回值赋给max*/printfC^Themaxmumvalueis%d\n",max);getch();)intmaxmum(intx,inty,intz){intmax;max=x>y?x:y;/*求最大值*/max=max>z?max:z;retum(max); /*返回最大值*/如果在定义函数时不指定函数类型,系统会隐含指定函数类型为int型。因此maxmum()函数左端的int可以省略,但是为了程序清晰和安全,建议都加以声明为好。另外,对形参类型的声明可以放在函数定义的第2行,即可以写成如下形式(以上面maxmum()函数为例):intmaxmum(x,y,z)intx,y,z;/*intmaxmum(x,y,z)intx,y,z;/*指定形参x,y*//*对形参指定类型*//*函数体中的声明部分/*函数体中的声明部分*/但我们推荐采用前面那种标准(ANSI)的书写方式,即对函数形参类型的指定放在函数名后面的括号内。5.1.2实训定义及使用函数实训目的.熟悉无参函数和有参函数的概念和格式。.理解什么是函数原型,怎样声明函数原型。.掌握无参函数与有参函数的声明和定义格式。实训内容要求:自定义两个函数,分别求给定值的绝对值和平方根。.自定义平方根函数sqrt()和求绝对值函数fabs()实现数学计算操作。(1)启动TurboC系统,进入编辑环境;(2)定义设计求给定值的绝对值函数fabs(),代码是:floatfabs(floatx)/*求x的绝对值*/if(x<0){x=-x;} /*如果x是负数则返回其相反数,否则直接返回*/return(x);(3)定[求平方根函数sqrt(),首先我们分析设计思路:我们利用Newton迭代法求给定值的绝对值的平方根,下面给出Newton迭代法求平方根的算法过程:设置猜测初值为1;如果I猜测值*猜测值・Xlv£,则转到④;(£指的是一个很小的数)设置新的猜测值为(X/猜测值+猜测值)/2,返回到②;④猜测值就是满足要求的X的平方根。根据算法,我们设计定义的求平方根函数sqrt()的代码是:floatsqrt(floatx) /*牛顿迭代法求实数x的平方根*/floatEpsilonNum,GuessNum;EpsilonNum=IE-5;GuessNum=1.0;while(fabs(GuessNum*GuessNum-fabs(x))>=EpsilonNum)GuessNum=(fabs(x)ZGuessNum+GuessNum)/2.0;/*调用自定义fabs()*/retum(GuessNum);.调用自定义函数,设计主程序功能界面,其代码如下:#include<stdio.h>#include<conio.h>floatsqrt(float);floatfabs(float);voidmain(){floatFloatNum,Result;/*定义输入变量和输出变量*/charSelectNum;while(l) /*显示功能菜单*/clrscr();puts(MPleaseselectfunction:**);puts(H\tl.fabs(X)n);puts("\t2.sqrt(X)");puts(n\t3.Exit");SelectNum=getche();switch(SelectNum)/*根据所选功能菜单,调用标准数学库*/{caseT:/*取x的绝对值*/printf("\nPleaseinputxforfabs(x):n);scanf(n%r,&FloatNum);Result=fabs(FloatNum);printf("fabs(x)=%t\n,',Result);getch();break;case2: /*求x的平方根*/printf(n\nPleaseinputxforsqrt(x):");scanf(n%f\&FloatNum);Result=sqrt(fabs(FloatNum));printf("sqrt(x)=%f\nn,Result);getch();break;case3,:return;重要提示:在上机调试程序时,请将上述自定义的两个函数代码加到主函数main。之前或々后。.保存上述设计的代码,编译、调试、连接并运行,记录运行结果。实训思考.对于本实例,为什么程序的首部不能放语句#include<math.h>?.在程序设计中函数sqrt()中调用函数fabs()的形式称为什么?.函数般为什么都需要原型?在哪些情况下可以不用函数原型?.1.3函数调用.函数调用形式当在程序中定义了函数之后,就意味着该函数将会在程序中被执行,但是怎样执行的呢?先看一个实例。【例5.5]求两个数阶乘和的函数调用实例。先设计两个函数:求给定数的阶乘函数factorial(),求两个阶乘和函数sum(),longfactorial(intn)(longrtn=I;inti;for(i=l;i<=n;i++)rtn*=i;retum(rtn);longsum(inta,intb)longcl,c2;cl=factorial(a);c2=factorial(b);retum(cl+c2);然后,我们写一个主函数main。,其代码如下:#include<stdio.h>#include<conio.h>longsum(inta,intb);longfactorial(intn);main()intnl,n2;longResult;clrscr();printfC'Pleaseinputtwointegernumbers(n1andn2):n);scanf(M%d%dM,&nl,&n2);Result=sum(nl,n2);printf(HResult=%Id".Result);getch();当从键盘上输入a,b的值为3和4后,程序执行结果如下:Pleaseinputtwointegernumbers(nlandn2):34/Result=30通过例5.5,我们可以看到,在主函数main()中使用函数sum(),函数sum()中又使用函数factorial()。这就是函数的调用。在TurboC中,函数调用时直接使用函数名和实参的方法,也就是将要赋给被调用函数的参量,按该函数说明的参数形式传递过去,然后进入子函数运行,运行结束后再按子函数规定的数据类型返回一个值给调用函数。使用TurboC的库函数就是函数简单调用的方法。值得注意的是:函数不能嵌套定义,函数间可以互相调用,但不能调用main()函数。调用函数的一般形式如下:函数名(实参列表);其中,实参是有确定值的变量或表达式,各参数间需要用逗号分开。在实参表中,实参的个数与顺序必须和形参的个数与顺序相同,实参的数据类型必须和对应的形参数据类型相同。若为无参数调用,则调用时函数名后的括号不能省略。C中函数在程序中的调用可以有以下几种方式。.函数调用方式首先我们看例5.6,请上机运行程序,并注意执行结果,考虑函数不同的调用方式。【例5.6]不同函数调用方式实例。#include<stdio.h>#include<conio.h>intmax(int,int,int);main()(inta,b,c,result1,result2;clrscr();printf("Pleaseinputtwointegernumbers(aandbandc):'*);scanf("%d,%d,%d",&a,&b);result1=3*maxmum(a,b,c);printf("Middleoftwonumbers,maxnumberis:%d\n,',resultl);result2=maxmum(a,maxmum(b,resultl),c);printf(HMiddleoftwonumbers,maxnumberis:%d\n,',result2);printf(MMiddleoftwonumbers,maxnumberis:%d\n”,maxmum(a,b,result2));getch();intmaxmum(intx,inty,intz){intmax;max=x>y?x:y; /*求最大值*/max=max>z?max:z;retum(max); /*返回最大值*/通过观察上述程序段,我们可以看出,C中函数调用按函数在程序中出现位置来分,可以有以下三种调用方式:(1)函数语句,即把函数调用当作一条语句。如例中的标准函数clrscr()、getch()o(2)函数表达式,即函数出现在一个表达式中,要求函数带回一个确定的值以参加表达式运算。如例中的c=3*maxmum(a,b)o(3)函数参数,即函数调用作为一个函数的实参。如例中的result2=maxmum(a,maxmum(b,result1),c)o实际上,例中的printf(°Middleoftwonumbers,maxnumberis:%d\nH,maxmum(a,b,result2));语句也是把maxmum(a,b,result2)作为printf函数的一个参数。【例5.7]打印出0到9的平方值#include<stdio.h>#include<conio.h>intsqu(intin);voidmain()intx,result;clrscr();fbr(x=0;x<=9;x++)result=squ(x);/*y=x*x*/printf("Thesquareof%dis%d\n",x,result);getch();for(x=0;x<=9;++x)printf("Thevalueofsqu(%d)is%d\n*',x,squ(x));getch();intsqu(intin)intsquare;square=in*in;retum(square);这个程序的两个循环都是打印出。到9的平方值。第一个循环中的函数squ()的返回值由变量result来接收;第二个循环中,函数squ()的返回值直接用做printf函数的参数。值得提醒的是:不论在什么情况下,只要进行了有参函数的调用,就必须要求实参与形参的个数相等,类型和参数顺序也一致。但在C的标准中,实参表的求值顺序并不是确定的。有的系统按自右向左的顺序计算,而有的系统则按自左向右的顺序计算。在大多数情况下,这个顺序对函数调用没什么影响,但有时我们必须考虑这个问题。请读者分析卜面的例5.8,看看程序输出什么值。我们的TurboC系统采用的是自右向左的计算顺序。【例5.8]实参表自右向左的求值顺序#include<stdio.h>#include<conio.h>intsum(intx,inty);main()inta=10,result;clrscr();result=sum(a,a=a+5);printf(nResult=%d\n",result);getch();intsum(intx,inty)return(x+y);执1上述程序后,结果显示:Result=30经过分析,可以看出我们所采用的C系统是按自右向左的结合规则运算的,否则本程序的运行结果应该是:Result=25(按自左向右的结合规则运算)。我们再看一个比较特殊的函数调用的例子。【例5.9】一个特殊的函数调用的例子。#include<stdio.h>intfunc(inta,intb);voidmain(){intk=4,m=l,p;p=func(k,m);printf(,'%d,'(,p);p=func(p,p=p+3);printf(M%d,H,p);p=func(printf(H%d,M,p),p);printf(H%dH,p);intfunc(inta,intb){intm=0,i=2;i+=m+1;m=i+a+b;retum(m);程序输出是:8,25,25,31 (请读者分析该程序。)通过以上几个实例的学习,我们基本熟悉了函数调用的一般形式。在这里,我们提醒读者,当函数没有参数时,在形参表中最好使用关键字void。虽然这不是绝对必要的,但是,对C的编译器检查参数是否正确是大有好处的。例如,假设有如下的函数原型:voidmyfunction(void);若用下面的语句调用:myfunction(myparam);则系统将会报告错误信息。所以,若函数不返回任何值或函数无任何参数时,我们建议把该类函数说明为void类型,要不然,系统编译器会理解为该函数是整型的(在前面我们介绍,当函数名前不加类型说明符时,系统默认为为整型函数),这样容易产生错误。5.1.4实训I 函数调用实训目的.熟悉函数定义格式及应用。.掌握函数调用含义与调用格式。.重点掌握函数调用的三种方式:作为函数语句、作为函数参数、作为函数表达式。实训内容要求:自定义一个检测函数,然后调用函数检测C程序中非语法类、非语义类逻辑错误,并打印相关信息。1.自定义能够检测int、char、int型数组、char型数组等类型的排错函数debug()。(1)启动TurboC系统,进入编辑环境;(2)自定义函数debug。,其函数原型是:voiddebug(charlet,charc_array[],intn_array[J,intasize,intnum,intopt);其中:opl是要使用的可选显其值为1「输出整型数:值为2,输出字符型;值为3,输出整型数组;值为3,输出字符型数组;其它值为错。代码如下:voiddebug(charlet,charc_array[],intn_array[],intasize,intnum,intopt)inti;switch(opt)/*整型数*/clrscr();printf("Thevalueis%dH,num);break;/*字符型*/clrscr();printf(nTheletteris%cn,let);break;/*整型数组*/clrscr();puts(HThenumberarraycontains:\nn);for(i=0;i<=asize;++i)printf(,,%dM,n_array[i]);break;/*字符型数组*/clrscr();puts(MThecharacterarraycontains\n");for(i=0;i<=asize;++i)printf("%c”,c_array[i]);break;default:clrscr();puts(n\nlnvalidoptionselected!1');break;puts(u\nPleasepressanykeytocontinue:'1);getch();.main。函数中定义int、char、int型数组、char型数组,调用debug。函数来检测给定值是否符合所定义的类型。代码为:voidmain()(inti,j,a[10];charch,b[10];for(i=5j=0;i<15;++i,++j){a[j]=j;b[j]=j;ch=,a,;debug(0,0,0,0,i,l); /*输出整型i的值*/debug(ch,0,0,0,0,2);/*输出字符型ch的值*/debug(0,0,a,10,0,3);/*输出整形数组a中各元素的值*/debug(0,b,0,10,0,4);/*输出字符型数组b中各元素的值*/debug(0,0,0,0,0,7); /*错*/重要提示:在上机调试程序时,请将上述自定义的debug。函数代码加到主函数main。之前或之后。.保存上述设计的代码,编译、调试、连接并运行,记录运行结果。实训思考.函数调用的基本格式是什么?.函数可以有哪些不同的调用方式?.若要进行float、double等类型数据的检查,则程序应如何扩充和修改?.1.5函数返回值在上面各实例定义的函数体的最后有一条return语句。一般使用return语句山被调用函数向调用函数返回值,该语句有卜列用途:(1)它能立即从所在的函数中退出,返回到调用它的程序中去。(2)返回一个值给调用它的函数。有两种方法可以终止子函数运行并返回到调用它的函数中:一是执行到函数的最后一条语句后返回,这表明一个函数中可以没有return语句;一是执行到语句return时返回。前者当子函数执行完后仅返回给调用函数一个数值(若函数被定义为void型,则不返回任何值)。若要返回一个值,就必须用return语句。只需在return语句中指定返回的值即可。返回语句一般有如下的形式:return;或return表达式;或return(表达式);【例5.10)不带返回语句的函数应用#include<stdio.h>#include<conio.h>inttotal(intx,inty);main()inta,b;clrscr();printf(MPleaseinputtwointegernumbers(aandb):");scanf("%d,%d”,&a,&b);total(a,b);getch();inttotal(intx,inty)printf(MThetotalis%d.\n",x+y);值得注意的是:表达式的类型必须和该函数的类型一致。对于不返回函数值的函数,函数的类型指定为void,这时,return语句不能带有表达式。另外,一个函数可以有一个以上的return语句。程序执行到哪个return语句,哪个relurn语句就起作用。请看下面的例子5.11。【例5.11]多return语句的函数应用本例子定义的函数作两个浮点数的除法运算。当除数为。时,函数不进行运算,只给出信息便返回调用它的函数。如果除数不为0,则返回计算结果。#include<stdio.h>#include<conio.h>floatdiv(floatx,floaty);main()floata,b,DivResult;clrscr();printf("Pleaseinputtwofloatnumbers(aandb):");scanf(M%f,%f;&a,&b);DivResult=div(a,b);printf("Theprogramexecuted!Resultis%f\n",DivResuIt);getch();floatdiv(floatx,floaty){if(y==O)printf(nproblemwithparameter!\nn);return;retum(x/y);.1.6实训I 建立和使用函数实训目的.掌握函数原型、函数定义格式与调用格式。.堂点掌握return语句形式、功能和函数调用时的返回值。.掌握字符串的求串长、查找、删除、插入、替换等操作。.培养和锻炼解决较复杂C程序设计的能力。实训内容要求:自定义相关的字符串操作函数,实现用户从键盘输入源字符串,再输入字符串1和字符串2。然后调用相应的功能函数,处理在源串中删除字符串1,并用字符串2来代替字符串1的操作,并将结果显示出来。.根据分析要求,我们设计五个自定义函数分别实现求串长、查找、删除、插入、替换。(1)启动TurboC系统,进入编辑环境;(2)根据系统需求,自定义相关的函数,设计的函数各代码如下。①求字符串长度函数原型:length(charstring]]);其实现代码是:intlength(charstring[]) /*求字符串的长度函数*/{intindex=0;while(string[index]!=AO')index++;return(index);②字符串查找函数原型:intfind_string(charstring1[],charstring2[]);其实现代码是:intfind_string(charstring1[],charstring2[])/*在string1中查找string2*/{chartemp;intindex1=0,index2;while(string1[index1]!='\01){index2=0;temp=string1[index1+index2];/*下面while循环是找子字符串位置*/while((String2[index2]!=W)&&(temp==string2[index2])){temp=string1[index1+++index2];if(string2[index2]==AO')/*找到*/return(index1);index1++;return(-1);/*未找到*/③字符串删除函数原型:remove_string(charstring[],intstart,intcount);其实现代码是:remove_string(charstring1],intstart,intcount)/*删除指定位置上的子串*/{intlen,index;chartemp;index=start+count;len=length(string);while(len>index){temp=stringfindex];string[index-count]=temp;index++;stringfindex-count]=*\0';(4)字符串插入函数原型:insert_string(charstring1[],charstring2[],intpos);其实现代码是:insert_string(charstring1[],charstring2[J,intpos)/*指定位置插入串 */{intlenl,len2,index;chartemp;lenl=length(stringl);len2=length(string2);index=lenl;while(index>=pos){temp=string1[index];string1[index+len2]=temp;index—;index=0;while(index<len2){temp=string2[index];string1[pos+index]=temp;index++;⑤字符串替换函数原型:intreplace(charsourcef],source,charstring1[],charstring2[J);其实现代码是:intreplace(charsourcel],source,charstring1[],charstring2[]){intstart,count;if((start=find_string(source,string1))!=-1){count=length(string!);remove_string(source,start,count);replace(source,stringl,string2);insert_string(source,string2,start);return(1);return(0);.在主函数中输入源串source、string1>串string2,调用自定义函数,并输出结果:main()charsource[80],stringl[80],string2[80];clrscr();printf(uSource_string:\n");scanf(n%sM,source);printf(uStringl:\nu);scanf(1,%sH,stringl);printf(nString2:\nn);scanf(n%sM,string!);if(replace(source,string1,string2))printf(M\n%s\n,',source);elseprintf("\nNotchanged.\n");getch();重要提示:在上机调试程序时,请将上述自定义的各个函数代码加到主函数main()之前或之后。.保存上述设计的代码,编译、调试、连接并运行,记录运行结果。实训思考.函数声明与函数定义有何区别?函数原型主要功能是什么?.函数定义与函数调用的格式是什么?.函数名后括号内的形式参数可以有哪些说明方式,试举例说明?.函数的返回值有可以是哪些?return语句的形式有哪些,其功能是什么?.在较复杂程序设计中,设计功能相对独立的函数模块有什么优越性?5.2形参与实参在调用函数时,大多数情况下,主调函数和被调函数之间有数据传递关系(这就是前面说到的有参函数)。在定义有参函数时函数名后括弧中的变量名称称为“形式参数”(简称“形参”),在主调函数中调用一个函数时,此函数名后面括弧中的参数称为“实际参数”(简称'‘实参”)。在TurboC语言中调用函数时,参数传递有两种方式:值调用和引用调用。在参数传递时,可以分为:简单变量作为函数参数传递、数组名作为函数参数传递和指针作为函数参数传递等多种情况。函数值调用和引用调用TurboC中调用函数时直接使用函数名和实参的方法,也就是将要赋给被调用函数的参量,按该函数说明的参数形式传递过去,然后进入子函数运行,运行结束后再按子函数规定的数据类型返回一个值给调用函数。使用TurboC的库函数就是函数简单调用的方法。对有参函数来说,只要存在函数调用,就会通过参数和返回值的传递交换信息。信息交换是在函数调用过程和返回过程中进行的。本节我们要讨论的是在函数调用时,按什么形式传递参数。.值调用值调用方法是把实参的值拷贝给形参,即调用函数向被调用函数传递的参数是变量本身值。在内存中,由于形参与实参占用不同的存储单元,这时形参值的变化将不影响实参的值。到目前为止,我们所举的函数实例中,采用的都是这种值调用。在这里不再赘述。.引用调用return语句可以向调用函数返回值,但这种方法只能返回一个参数,在许多情况下要返回多个参数,这时用return语句就不能满足要求。TurobC提供了另一种参数传递的方法,即引用调用,该方法是在调用时把实参的地址拷贝到形参,使用地址去访问实参。此时调用函数向被调用函数传递的参数不是变量本身,而是变量的地址,当子函数中向相应的地址写入不同的数值之后,也就改变了调用函数中相应变量(实参)的值(形参值变化影响实参值变化),从而达到了返回多个变量值的目的。在TurboC中,引用调用是通过指针实现的,在内存中,形参与实参占用相同的存储单元。引用调用的函数原型是:函数类型函数名(参数类型*参数1,参数类型*参数2,…);调用时的形式是:函数名(&参数1,&参数2,…)【例5.12]函数的引用调用示例。#include<stdio.h>#include<conio.h>voidswap(char*,char*); /*声明指针类型参数*/main(){charchl='A',ch2=B;clrscr();printf("调用函数swap()之前chi与ch2的值是:");printfC^ch1=%c\tch2=%c\nn,ch1,ch2);/*调用swap。前chi,ch2的值*/swap(&ch1,&ch2); /*引用调用*/printf("调用函数swap()之后chi与ch2的值是:");printf(Hch1=%c\tch2=%c\nn,ch1,ch2);/*调用swap。后chi,ch2的值*/getch();voidswap(char*x,char*y){chartemp;temp=*x;*X=*y;*y=temp;printf("形参的值:x=%c\ty=%c\nH,*x,*y);执;亍上述程序,运行结果如下:调用函数swap()之前chi与ch2的值是:chl=A ch2=B形参的值:x=B y=A调用函数swap。之后chi与ch2的值是:chl=Bch2=A在上面例5.12中,函数swap。的参数被声明为指针类型。并且,尽管swap。的操作结果在main。中输出,但我们仍然定义函数sw叩。为void类型。执行上述程序,我们会发现字符变量chi和ch2的值在函数swap。调用前后发生了改变。【例5.13】#include<conio.h>#include<stdio.h>voidsubfun(int*i,int*j);/*声明子函数*/intmain()inti,j;clrscr();printfC^Pleaseinputparamenter(i,j=?):n);scanf(M%d,%d”,&i,&j);/*从键盘输入2个整数*/printf(n\nlnmainbeforecalling:**/*输出此2数及其乘积*/ui=%-4dj=%-4di*j=%-4d\nM,i,j,i*j);subfun(&i,&j); /*以传送地址的方式调用子函数*/printf(M\nInmainaftercalling:"/*调用子函数后输出变量值*/Mi=%-4dj=%-4di*j=%-4d\nM,i,j,i*j);

getch();)voidsubfun(int*i,int*j)printf(n\nlnsubfunaftercalling:"/*子函数中输出变量值*/Mi=%-4dj=%-4di*j=%-4d\nM,*i,*j,*i**j);1其中:中表示指针i和j所指的两个整型数*i和*j之乘积。程序输出是:Pleaseinputparamenter(i,j=?):-12,11/i*j=-132i*j=210i*j=210TOC\o"1-5"\h\zInmainbeforecalling:i=-12 j=l1i*j=-132i*j=210i*j=210Insubfunaftercalling:i=-10 j=-21Inmainaftercalling:i=-10 j=-21注意:由于用户编写的函数一般在对其声明和定义时就规定了形式参数类型,因此调用这些函数时要求实参必须与子函数中形式参数的数据类型、顺序和数量完全相同,否则在调用中将会出错,得到意想不到的结果。5.2.2实训5.2.2实训值参数和地址参数的传递实训目的.理解函数的形式参数与实在参数的概念和应用。.重点掌握函数的值调用和引用调用原理。.掌握浮点数转化为字符串的方法、系统库函数fcvt()的使用。实训内容要求:自定义一个函数float2str(),调用系统库函数fcvt(),实现将给定的浮点数-3.1415926000和0.31415926转化为字符串的操作。1.根据需求,设计定义函数float2str()。(1)启动TurboC系统,进入编辑环境;(2)自定义函数float2str()o我们调用系统库stdlib.h中的函数fcvt(),来实现操作。函数fcvt()的原型是:char*fcvt(doublevalue,intndigit,int*dec,int*sign);其功能是将传入的浮点数转化为字符串并返回。其中:参数dec用于返回小数点位置;参数sign返回符号(负数返回1,正数返回0)。自定义函数float2str()的代码是:/*参数说明data:需转换的浮点数;s:输出字符串;len:转换后的长度*/voidfloat2str(doubledata,char*s,intlen)intdec,sign,i,curr;char*s1;curr=0;i=0;si=NULL;si=fcvt(data,len,&dec,&sign);/*函数fcvt()把一个浮点数转换为一个*//*字符串,参数dec保存小数点,sign保存负号*/if(sign){ /*如果是负数在结果前面加上一个符号s[curr++]=while(i<strlen(si)){if(i==dec&&(fabs(data)<1.0)){/*处理纯小数的情况*/s[curr+4-]=O;s[curr++]=7;elseif(i==dec){ /*加上小数点*/s[curr++]=7;s[curr+4-]=si[i++];s[curr]=W;/*标上字符串终结符'\0'*/2.在main()函数中调用函数float2str(),实现将给定的浮点数-3.1415926000和0.31415926转化为字符串的操作。其代码如下:#include"stdio.h"#include"math.h"#include"stdlib.h"#includeHconio.h"voidmain()/*主函数*/{doubletest;charstring1[20];intlen;clrscr();len=10;test=-3.1415926;float2str(test,string1,len);printf(nsource:%2.10f\nconvertstring:%s\n",test,string1);test=0.31415926;float2str(test,string1,len);printf(''source:%2.10f\nconvertstring:%s\nH,test,string1);getch();重要提示:在上机调试程序时,请将上述自定义的各个函数代码加到主函数main。之前或之后。3.保存上述设计的代码,编译、调试、连接并运行,记录运行结果。实训思考.浮点数转换为字符串的方法是怎样实现的?.形式参数与实在参数之间的区别是什么?在函数调用时,它们之间怎样传递参数?.怎样理解值调用与引用调用?.在函数调用时,有哪些形式可以作为参数来传递?函数能不能作为参数来传递?.函数调用时,对形参和实参有什么要求?简单变量作为函数的参数当实参是简单变量时,就是简单变量作为函数参数的情况。关于这种情况,我们在这里不再赘述。因为前面我们好多的实例采用的都是这种以简单变量作为函数的参数这种情形。数组作为函数的参数当实参是数组时,就是数组作为函数参数的情况。这时数组作为参数向被调用函数传递时,只传递数组的地址,而不是将整个数组元素都复制到函数中去,即用数组名作为实参调用子函数,调用时指向该数组第一个元素的指针就被传递给子函数。因为在TurboC中,没有下标的数组名就是一个指向该数组第一个元素的指针。在调用时作为实参的数组类型必须与对应形参类型相同。数组作为函数参数传递属于引用调用方式。此时,数组函数的原型可以有以下几种写法(设有整型数组array):intmyfunction(inlarray[10]);intmyfunction(intarray[]);intmyfunction(int*array);上述三种写法的作用是一样的,都是传递一个指针。注意:当传递数组的某个元素时,数组元素作为实参,此时按使用简单变量的方法使用数组元素。【例5.14】输入数组,最大的元素与第一个元素交换,最小的元素与最后一个元素交换,输出数组。#include<stdio.h>#include<conio.h>voidinput(intnumber[10]);voidmax_min(intarraylJ);voidoutput(int*array);main()intnumberf10];clrscr();input(number);max_min(number);output(number);getch();voidinput(intnumber[10J)inti;printf(nPleaseinputtennumbers:H);for(i=0;i<=9;i++)scanf(,,%d;',&number[i]);/*请注意此处的数据读入格式*/voidmax_min(intarray[10])int*max,*min;int*p,*arr_end;arr_end=array+10;max=min=array;for(p=array+1;p<arr_end;p++){if(*p>*max)max=p;elseif(*p<*min)min=p;*p=array[0];array[0]=*max;*max=*p;*p=array[9];array[9]=*min;*min=*p;voidoutput(intarray[10])int*p;for(p=array;p<=array4-9;p++)printf(,'%4d,',*p);执行上面程序,运行结果是:Pleaseinputtennumbers:10123100177-102322/17712310022102223-10请读者考虑,若数据输入格式是:10,1,2,3,100,177,-10,23,22/.结果会怎样?指针作为函数的参数当实参是指针时,就是指针作为函数参数的情况。这时,也是只传递实参的地址,而不是拷贝数据给形参。显然,指针作为函数参数是典型的引用传递方式。【例5.15】指针作为函数参数与一般变量作为函数参数的不同之处。#include<stdio.h>#include<conio.h>voidfixup(intnuts,int*fruit);voidmain()intpecans,apples;pecans=100;apples=101;clrscr();printf(MThestartingvaluesare%d%d\nH,pecans,apples);fixup(pecans,&apples);printf(nTheendingvaluesare%d%d\n",pecans,apples);getch();voidfixup(intnuts,int*fruit)printf(nThevaluesare%d%d\n",nuts,*fruit);nuts=135;*fruit=172;printfC'Thevaluesare%d%d\n",nuts,*fruit);实训 函数参数传递的形式实训目的.理解函数的形参与实参的概念和应用。.掌握函数的值调用和引用调用的含义。.掌握简单变量、数组和指针作为函数参数进行传递的形式。.熟悉带参数的main。函数格式。实训内容要求:学习利用main。函数的ARGC、ARGV参数来实现命令行参数的应用,同时调用系统的声音函数进行报错处理操作。具体要求是使用带参数的main。函数实现求任意多个数的最大值。.分析程序要求。(1)带参数的main()函数原型是:main(intargc,char*argvLJ,char*env[J)»其中:前两个参数作为用户输入命令后的返回值被保存,argc为int型变量,它存储用户命令中函数的个数,值得注意的是可执行文件名也被计数了一次,即用户实际输入的命令中所带参数的个数等于argc-l«(2)对命令行参数的实例解释,若在DOS状态下输入如下的命令行:copysource.txtd:\则source.txt,d:\是copy命令的命令行参数,可执行文件名copy作为第一个参数存储在argv中,argc的值为3。这表明,当argc的值小于2时则用户没有输入参数。argv[]是一个字符串指针数组,其每个元素按顺序存储相应的命令行参数。对于上面的命令行,对应参数值为:argc=3argv[0]指向字符串source.txtargv"]指向字符串d:\(3)对本实训的理解是:在main。中定义一个求最大值函数maxmum。,在命令行输入相关命令,将相应的值通过main()的ARGC、ARGV参数送到程序中,然后执行程序并输出。.根据要求,设计主函数main()程序,其代码是:#include<stdio.h>#include<dos.h>#include<stdlib.h>#include<string.h>#defineMAX100 /*设置最大可求的整数数目*/#defineNatoi(argv[l]) /*将命令行得到的字符转化为整型值*/#definemaxmum(a,b)(a<b)?b:a/*定义求两个数的最大值宏*/voidmain(intargc,char*argv[]){inti,j;intslMAX];clrscr();if(argc<2){ /*若没有参数*/printfC'error:Nocommandline.\nH);printf(HYoumustinputthelengththatyouwant.\n");printf(nexample:maxmum5\n");exit(l);if(argc>3){ /*若参数超过数目*/printfC^rror:Toomanypatemers!\nH);printf(nTheexactstyle:maxN\n");exit(l);if(argc==3&&!strcmp(argv[2],nsoundu)){/*如果用户写了可选参数sound*/sound(lOOO);delay(3000);nosound();/*delay。延时函数*/sound(800); delay3000);nosound(); /*sound。声音函数*/printf(HPleaseinputnumbers:\nM);for(i=0;i<N;i++)scanf(M%dH,&s[i]);for(j=0;j<N;j++)s[j+1]=maxmum(s[j],s[j+1]);printf(MThemaxnumberis%d.\nn,s[N-lJ);printf(、END");getch();.保存上述设计的代码,编译、调试、连接并运行,记录运行结果。说明:本程序执行过程是当编辑好以上代码经调试正确后保存,取名为example.C,编译、连接运行后生成example.exe文件;进入DOS环境,在当前命令符行输入命令:C:\TC>example5/(5表示输入数据最大个数,即求五个数中的最大值)Pleaseinputnumbers:124567820/Themaxnumberis78./*空行*/END实训思考.带参数的main。函数中前两个参数如何理解?.main()能否有返回值?.若求6个数中的最大数,且有声音提示信息,则程序如何输入执行?给出步骤。5.3返回指针值的函数和指向函数的指针返回指针值的函数函数的reum语句可以带回一个整型值、字符值、实型值等,也可带回指针型数据(即地址)。返回值为一指针的函数称为指针类型函数。指针类型既不是整型,也不是无符号整型,它是存储器的地址。因此,指针类型函数也必须用函数原型进行专门的说明。指针类型函数原型的一般形式如下:指针类型*函数名(参数类型表);例如:int*a(intx,inty);float*b(floatn);char*str(charch);请注意,在上述三个指针函数的原型中,*a、*b、*str的两侧没有括号,这即表示函数a、b、str是指针型函数(函数返回值是指针)。这里要与(*公、(*b)、(*str)这种定义形式区别开(关于这种类型的定义我们在下一节内容中再作介绍)。【例5.16]等待从键盘输入一字符串,再等待输入要查找的字符,然后调用match。函数在字符串中查找该字符。若有相同字符,则返回一个指向该字符串中这一位置的指针,如果没有找到,则返回一个空(NULL)指针。#include<conio.h>#include<alloc.h>char*match(charc,char*s);intmain(){chars[40],c,*str;str=malloc(40); /*为字符串指什分配内存空间clrscr();printf(HPleaseinputcharacterstring:*');gets(s); /*键盘输入字符串*/printf(*'Pleaseinputonecharacter:'1);c=getche(); /*键盘输入字符*/str=match(c,s); /*调用子函数*/putchar(\n');puts(str); /*输出子函数返回的指针所指的字符串*/getch();char*match(charc,char*s){inti=0;while(c!=s[ij&&s[i]!^n,) /*找字符串中指定的字符*/i++;retum(&s[i]); /*返回所找字符的地址*/【例5.17】指针类型函数的例子有若干学生成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。#include<stdio.h>#include<conio.h>float*search(float(*pointer)[4],intn); /*指针类型函数原型*/main(){ floatscore[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66});float*ptr;inti,code;clrscr();print^'Tleaseinputthenumbersofstudent(code):*');scanf("%d'\&code);printf(nThescoresofNo.%dare:\n",code);ptr=search(score,code);for(i=0;i<4;i++)printf("%5.2f\t”,*(ptr+i));getch();float*search(float(*pointer)[4J,intn){float*pt;pt=*(pointer+n);retum(pt);运行上述程序,显示如下结果:Pleaseinputthenumbersofstudent(code):I/ThescoresofNo.1are:56.0089.0067.0088.00请注意程序中指针变量ptr、pt和pointer的区别。如果将search()函数中的语句pt=*(pointer+n);改为pt=(*pointer+n);则程序的运行结果是:Pleaseinputthenumbersofstudent(code):1/ThescoresofNo.1are:70.0080.0090.0056.00这时得到的不是第二个学生(学生序号从0算起)的成绩,而是二维数组中score[0][l]开始的4个元素值。请读者仔细消化本例中指针变量的含义和用法,并分析为什么会出现意外结果?指向函数的指针.函数指针在上一小节的例5.16中,我们用了三个比较特殊的指针类型变量ptr、pt和pointer。在实际的程序设计中,我们不仅可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时分配给一个入口地址。这个入口地址称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。我们先看一个例子。【例5.18】指向函数的指针示例。#include<stdio.h>#include<conio.h>intsum(int,int);•nt(*p)();main()inta,b,Result;clrscr();p=sum;printf("Inputtwonumbers(aandb):n);scanf(M%d%d*,,&a,&b);Result=(*p)(a,b);printf(uThesumis%d\n",Result);getch();intsum(intxjnty)return(z);在)上述程序中,语句“int(*p)();"定义了p是一个指向函数的指针变量,此函数带回整型的返回值。注意*p两侧的括号不可省略。请读者区别int(*p)()和int*p()的区别?此外,赋值语句“p=sum;"的作用是将函数sum()的入口地址赋给指针变量p。调用*p就是调用函数sum。请读者注意p是指向函数的指针变量,它只能指向函数的入口处而不可能指向函数中的某一条指令处,因此,*(p+l)这种表示函数的下一•条指令的做法是错误的。请读者分析语句:Result=(*p)(a,b)与Result=sum(a,b)是否等价?说明:(1)指向函数的指针变量一般定义形式为:数据类型(*指针变量名)();这里“数据类型”是指函数值的返回类型。(2)函数的调用可以通过函数名调用,也可以通过函数指针调用(即用指向函数的指针变量调用)。函数指针可以这样赋值:函数指针变量=函数名;如例5.18中的语句:p=sum;(此处函数名后不能有括号)语句Result=(*p)(a,b);表示“调用由p指向的函数,实参为a,b。得到的函数值赋给变量Resulto.指向函数的指针作函数参数有了函数指针,就可以实现整个函数在函数之间的传递,也就是函数作为实参传递给其它函数。函数指针常见用途之一是把指针作为参数传递到其他函数。这个问题是C语言应用中比较深入的部分,本书中只是简单介绍,以使学生们在今后用到时不陌生、不困惑。【例5.19】由键盘输入三角形的边长a,b和c。然后由函数area()用海伦公式计算该三角形的面积。计算结果返回主函数,并在主函数中输出。计算公式如下:t=(a+b+c)/2.0area=y]t(tb)(t-c)程序代码如下:#include<stdio.h>#include<stdlib.h>#include<math.h>#include<conio.h>main(){doublea,b,c,result;/*定义函数指针*//*定义函数指针*/doublearea(double,double,double);/*声明函数*/charsa[20],sb[20],sc[20];clrscr();printf(uPleaseinputthreeedgenumbers(aandbandc):\nu);gets(sa);gets(sb);gets(sc);a=atof(sa);b=atof(sb);c=atof(sc);func_ptr=area;/*给函数指针赋值*/result=(*func_ptr)(a,b,c);printf("Result=%15.10f\nM,result);getch();doublearea(doublea,doubleb,doublec){doublet,area;t=(a+b+c)/2.0;area=t*(t-a)*(t-b)*(t-c);retum(sqrt(area));当k键盘输入3,4和5时,程序执行结果如下:Pleaseinputthreeedgenumbers(aandbandc):/Z/Result=6.0000000000在这个实例中,最为重要的就是必须先将函数area()的指针赋给函数指针变量func_ptr。另外,在本例中使用了一个库函数atof(),它的功能是把数字字符串转换为双精度浮点数。其原型是:doubleatof(constchar*s);类似的函数还有atoi()、atol()等,这些函数说明都在头文件stdlib.h中,有兴趣的读者可以继续深入。5.4函数嵌套调用与递归调用函数嵌套调用C语言中函数的定义都是互相平行的,独立的。一个函数的定义内不能包含另一个函数。这就是说C语言是不能嵌套定义函数的,但C语言允许嵌套调用函数。所谓嵌套调用就是在调用一个函数并执行该函数中,又调用另一个函数的情况。先看一个例子。【例5.20]编写一个计算m中取n的组合的程序。在本例中,组合数的计算用函数functionA()进行,而它需要的阶乘计算由函数functionB()进行。计算组合数的公式如下:n!(m-n)!本例程序的结构是:主函数main()调用函数functionA(),而函数functionA()上次调用

函数functionB(),计算m!,n!,(m-n)!o计算结果返回给主函数进行输出。m和n由键盘输入。程序代码如下:#include<stdio.h>#include<conio.h>longfunctionA(long,long);longfunctionB(long);main(){longm,n,c;clrscr();printf("Pleaseinputtwonumbers(mandn):H);scanf(*'%ld%ld",&m,&n);c=f

温馨提示

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

评论

0/150

提交评论