信息学奥赛DEV C++基础教程之函数.docx

上传人:b****6 文档编号:4448349 上传时间:2022-12-01 格式:DOCX 页数:25 大小:448.83KB
下载 相关 举报
信息学奥赛DEV C++基础教程之函数.docx_第1页
第1页 / 共25页
信息学奥赛DEV C++基础教程之函数.docx_第2页
第2页 / 共25页
信息学奥赛DEV C++基础教程之函数.docx_第3页
第3页 / 共25页
信息学奥赛DEV C++基础教程之函数.docx_第4页
第4页 / 共25页
信息学奥赛DEV C++基础教程之函数.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

信息学奥赛DEV C++基础教程之函数.docx

《信息学奥赛DEV C++基础教程之函数.docx》由会员分享,可在线阅读,更多相关《信息学奥赛DEV C++基础教程之函数.docx(25页珍藏版)》请在冰豆网上搜索。

信息学奥赛DEV C++基础教程之函数.docx

信息学奥赛信息学奥赛DEVC+基础教程之函数基础教程之函数DEV教程第六章函数第22课:

函数前面我们曾经学习了程序设计中的三种基本控制结构(顺序、分支、循环)。

用它们可以组成任何程序。

但在应用中,还经常用到子程序结构。

通常,在程序设计中,我们会发现一些程序段在程序的不同地方反复出现,此时可以将这些程序段作为相对独立的整体,用一个标识符给它起一个名字,凡是程序中出现该程序段的地方,只要简单地写上其标识符即可。

这样的程序段,我们称之为子程序。

子程序的使用不仅缩短了程序,节省了内存空间及减少了程序的编译时间,而且有利于结构化程序设计。

因为一个复杂的问题总可将其分解成若干个子问题来解决,如果子问题依然很复杂,还可以将它继续分解,直到每个子问题都是一个具有独立任务的模块。

这样编制的程序结构清晰,逻辑关系明确,无论是编写、阅读、调试还是修改,都会带来极大的好处。

在一个程序中可以只有主程序而没有子程序(本章以前都是如此),但不能没有主程序,也就是说不能单独执行子程序。

在此之前,我们曾经介绍并使用了C+提供的各种标准函数,如abs(),sqrt()等等,这些系统提供的函数为我们编写程序提供了很大的方便。

比如:

求sin

(1)+sin

(2)+sin(100)的值。

但这些函数只是常用的基本函数,编程时经常需要自定义一些函数。

一、函数的概念函数是一种对应关系的描述,即确定了函数自变量到函数值之间的对应关系。

与函数相关的概念如下。

自变量:

函数计算时使用的数据,自变量的取值范围称为函数的定义域。

在程序设计中,函数的自变量称为参数,定义域由参数的类型决定。

函数值:

函数的计算结果,它的取值范围称为值域。

在程序设计中函数值称为函数返回值,返回值的类型称为函数类型。

表达式:

数学中函数的计算过程由表达式来说明,在程序设计中,一个函数的处理过程往往比一个表达式所描述的内容要复杂得多,需要一段程序来描述所有的操作,这就是C+语言一个函数的函数体。

二、函数的分类分为系统函数和用户定义函数(也叫自定义函数)1、系统函数系统函数取出由C+语言系统提供的函数(也称库函数)。

这些函数可以从语言系统的手册上查阅其功能和使用方法,用户不需自己再进行定义说明,就直接调用。

例如我们已经学过的标准函数abs、sqrt、max、min等。

例1:

编写求n!

的函数。

参考程序:

#includeusingnamespacestd;intfact(intn)/fact为函数名;括号里面的为形式参数,简称为形参。

inti,t=1;if(n0)coutError!

endl;return0;for(i=2;i=n;i+)t=t*i;returnt;intmain()intm,x;coutm;x=fact(m);/函数调用,返回值赋给变量x。

coutm!

=xendl;return0;三、函数的定义1.函数定义的语法形式数据类型函数名(形式参数表)/函数的首部/函数体;说明部分;语句部分;关于函数的定义有如下说明:

1、函数的数据类型是函数的返回值类型(若数据类型为void,则无返回值)。

2、函数名是标识符,一个程序中除了主函数名必须为main外,其余函数的名字按照标识符的取名规则可以任意选取,最好取有助于记忆的名字。

3、形式参数(简称形参)表可以是空的(即无参函数);也可以有多个形参,形参间用逗号隔开,不管有无参数,函数名后的圆括号都必须有。

形参必须有类型说明,形参可以是变量名、数组名或指针名,它的作用是实现主调函数与被调函数之间的关系,通常将函数所处理的数据、影响函数功能的因素或者函数处理的结果作为形参。

在被调用函数中的参数被称为形参。

4、函数中最外层一对花括号“”括起来的若干个说明语句和执行语句组成了一个函数的函数体。

由函数体内的语句决定该函数功能。

函数体实际上是一个复合语句,它可以没有任何类型说明,而只有语句,也可以两者都没有,即空函数。

5、函数不允许嵌套定义。

在一个函数内定义另一个函数是非法的。

但是允许嵌套使用。

6、函数在没有被调用的时候是静止的,此时的形参只是一个符号,它标志着在形参出现的位置应该有一个什么类型的数据。

函数在被调用时才执行,也就是在被调用时才由主调函数将实际参数(简称实参)值赋予形参。

这与数学中的函数概念相似,如数学函数:

f(x)=x2+x+1这样的函数只有当自变量被赋值以后,才能计算出函数的值。

三、函数的调用函数的一般调用形式为:

函数名(实在参数1,实在参数2,)若实在参数的个数多于一个时,各实在参数之间用逗号隔开。

若函数无形参,调用形式为:

函数名()函数名后的一对圆括号不可少。

例2:

任意输入10组三角形的三边,求其面积。

【问题分析】:

已知三角形的三条边求其面积,用海伦公式:

P=(a+b+c)/2S=【算法设计】:

我们可以定义一个已知三角形三条边分别为a,b,c,求其面积的函数,设为area(a,b,c)。

程序如下:

#include#includeusingnamespacestd;doublearea(doublex,doubley,doublez)doubles1,p;p=(x+y+z)/2;s1=sqrt(p*(p-x)*(p-y)*(p-z);returns1;intmain()doublea,b,c;inti;for(i=1;i=10;+i)coutinputa,b,cabc;if(a+b=c)|(a+c=b)|(b+c=a)coutdataerror!

endl;/判断三角形是否合法;elsecouts=area(a,b,c)endl;return0;注意:

在函数说明中,如果形参的个数不止一个,那么在程序中调用函数的实参个数一定要与形参的个数一致;第一个实参对应第一个形参,第二个实参对应第二个实参,次序不能对调。

例3:

无参函数编程序打印如下图:

、-|-|-|-【问题分析】:

共有四个横行,每行10个“-”;共有四个竖行,每行4个“|”【算法设计】:

设计一个专门的函数只输出横线。

另外一个函数专门输出竖线。

参考程序:

#include#includeusingnamespacestd;voidhx()inti;for(i=1;i=10;i+)cout-;coutendl;voidsx()intj;cout|;for(j=1;j4;j+)coutsetw(3)|;coutendl;intmain()intk;for(k=1;k4;k+)hx();sx();hx();return0;练习:

求:

1!

+2!

+3!

+10!

第23课:

函数例1:

使用标识符sum_double求两个浮点数之和。

【问题分析】:

在C+的库函数中,很多由常用程序组成的函数可以直接调用,它们用的就是标识符。

【算法设计】:

函数sum_double(doublex,doubley)有两个参数,参数的类型为双精度实数。

调用函数时要传递两个双精度实数。

参考程序:

#includeusingnamespacestd;doublesum_double(doublex,doubley)returnx+y;intmain()doublea,b,sum;coutab;sum=sum_double(a,b);coutsum=sumendl;return0;四、函数调用的传递方式函数的调用分为传值调用和传地址调用两种,这两种调用的区别在于参数的传递机制不同。

1、传值调用在被调用的函数中形参的改变不会影响到实在参数的值,换句话说,实参的值是被复制给了形参名字所标识的一个对象中,函数内部对形参的改变并不能影响到实在参数。

2、传地址调用传地址调用时,实参用的是地址值,而形参则是我们之后要学到的指针变量。

在这章我们先掌握它的调用形式和作用。

在C+语言中,数据只能从实际参数单向传递给形式参数,称为“按值”传递。

例2:

以下两个程序,哪一个能交换两个数的值。

参考程序一:

#includeusingnamespacestd;voidswap(inta,intb)inttmp=a;a=b;b=tmp;intmain()intc=1,d=2;swap(c,d);coutcdendl;return0;参考程序二:

#includeusingnamespacestd;voidswap(int&a,int&b)inttmp=a;a=b;b=tmp;intmain()intc=1,d=2;swap(c,d);coutcdendl;return0;在参考程序一中,虽然在swap函数中交换了a,b两数的值,但是在main中却没有交换。

因为swap函数只是交换c,d两变量的值,主程序传递给函数c、d的是变量的值在参考程序二中,因为swap函数的参数为传地址调用,所以,在函数swap中修改a,b的值相当于在主函数main中修改c,d的值。

所以参考程序一运行完成后结果为:

12;参考程序二运行完成后结果为:

21。

除了用地址表示符“&”表示传的是地址之外,也可以用指针表示。

参考程序三:

#includeusingnamespacestd;voidswap(int*a,int*b)inttmp=*a;*a=*b;*b=tmp;intmain()intc=1,d=2;swap(c,d);coutcdendl;return0;五、函数的全程变量、局部变量及它们的作用域在函数外部定义的变量称为外部变量或全局变量,在函数内部定义的变量称为内部变量或局部变量。

1、全局变量全局变量的作用域是从变量定义的位置起直至本源文件结束止,即从定义位置之后的所有函数都可以访问该全局变量。

下面通过例子来说明这一点。

例3:

全局变量的应用。

#include#includeusingnamespacestd;intx,y;/定义全局变量x,yintfun1(ints)/访问全局变量x,yx=10;y=x*s;returnx+y;floata,b;/定义全局变量a,bvoidfun2(intc)coutx=xy=ymn;coutfun1(m)endl;fun2(n);couta=ab=bendl;/访问全局变量a,breturn0;当在键盘上输入69后程序的执行结果是:

70x=10y=60a=0b=0程序中主函数先调用fun1(),实参是6,将实参值传给形参s(即s=6),函数fun1中x值为10,y值为60,return语句将xy的和返回,fun1()结束。

主函数输出返回值之后,调用fun2(),在fun2()中输出全局变量x和y的值,之后返回主函数。

在主函数中输出全局变量a和b值。

由于a、b在声明时未赋初值,系统的默认值为0。

使用全局变量的说明:

在一个函数内部,既可以使用本函数定义的局部变量,也可以使用在此函数前定义的全局变量。

全局变量的作用是使得函数间多了一种传递信息的方式。

如果在一个程序中多个函数都要对同一个变量进行处理,即共享,就可以将这个变量定义成全局变量,使用非常方便,但副作用也不可低估。

过多地使用全局变量,会增加调试难度。

因为多个函数都能改变全局变量的值,不易判断某个时刻全局变量的值。

过多地使用全局变量,会降低程序的通用性。

如果将一个函数移植到另一个程序中,需要将全局变量一起移植过去,同时还有可能出现重名问题。

全局变量在程序执行的全过程中一直占用内存单元。

全局变量在定义时若没有赋初值,其默认值为0。

2、局部变量局部变量的作用域是在定义该变量的函数内部。

换句话说,局部变量只在定义它的函数内有效。

在一个子程序内定义的变量也是局部变量,其作用域是该子程序。

函数的形参也是局部变量。

由于局部变量的作用域仅局限于本函数内部,所以,在不同的函数中变量名可以相同,它们分别代表不同的对象,在内存中占据不同的内存单元,互不干扰。

一个局部变量和一个全局变量是可以重名的,在相同的作用域内局部变量有效时全局变量无效。

即局部变量可以屏蔽全局变量。

练习一:

编程求5!

+7!

+9!

+11!

的值。

练习二:

求正整数2和100之间的完全数。

完全数:

因子之和等于它本身的自然数,如6=1+2+3练习三:

如果一个自然数是素数,且它的数字位置经过对换后仍为素数,则称为绝对素数,例如13。

试求出所有二位绝对素数。

第24课:

函数的综合运用例1:

计算如图多边形的面积。

【问题分析】:

从图中可以看出,五边形的面积是三个三角形面积之和。

【算法设计】:

五边形的5条边分别设为b1、b2、b3、b4、b5,两条对角线分别为b6、b7,然后利用海伦公式分别求出三个三角形的面积,三个三角形的面积之和即为五边形的面积。

参考程序:

#include#include#includeusingnamespacestd;doublearea(doublea,doubleb,doublec);intmain()doubleb1,b2,b3,b4,b5,b6,b7,s;coutpleaseinputb1,b2,b3,b4,b5,b6,b7:

b1b2b3b4b5b6b7;s=area(b1,b5,b6)+area(b2,b6,b7)+area(b3,b4,b7);printf(s=%10.3lfn,s);return0;doublearea(doublea,doubleb,doublec)doublep=(a+b+c)/2;returnsqrt(p*(p-a)*(p-b)*(p-c);此程序中的函数位置与前面的函数不同,放在了主程序的后面。

只在主程序前面放了一个函数的声明。

如果不做声明就放在了主程序后,是会出错的。

六、函数的声明调用函数之前先要声明函数原型。

在主调函数中,或所有函数定义之前,按如下形式声明:

类型说明符被调函数名(含类型说明的形参表);如果是在所有函数定义之前声明了函数原型,那么该函数原型在本程序文件中任何地方都有效,也就是说在本程序文件中任何地方都可以依照该原型调用相应的函数。

如果是在某个主调函数内部声明了被调用函数原型,那么该原型就只能在这个函数内部有效。

函数原型和函数定义在返回值类型、函数名和参数个数与类型必须完全一致,否则,就会发生编译错误。

下面对max()函数原型声明是合法的。

intmax(intx,inty);也可以:

intmax(int,int);可以看到函数原型声明与函数定义时的第一行类似,只多了一个分号,成为了一个声明语句而已。

2、函数的返回值在组成函数体的各类语句中,值得注意的是返回语句return。

它的一般形式是:

return(表达式);其功能是把程序流程从被调函数转向主调函数并把表达式的值带回主调函数,实现函数的返回。

所以,在圆括号表达式的值实际上就是该函数的返回值。

其返回值的类型即为它所在函数的函数类型。

当一个函数没有返回值时,函数中可以没有return语句(在TC+和VC+,函数类型定义为void,可以没有return语句;函数类型定义为int,必须有返回值),直接利用函数体的右花括号“”,作为没有返回值的函数的返回。

也可以有return语句,但return后没有表达式。

返回语句的另一种形式是:

return;这时函数没有返回值,而只把流程转向主调函数。

例2:

定义一个函数check(n,d),让它返回一个布尔值。

如果数字d在正整数n的某位中出现则送回true,否则送回false。

例如:

check(325719,3)=true;check(77829,1)=false;【问题分析】:

本题是利用函数求位数是否相同的题目,函数的返回值为布尔(boolean)变量。

【算法设计】:

利用While循环语句,一位一位的取出给定数字n各位上的数,与给的数d比较,如果相等则函数返回true,否则则返回false。

参考程序:

#includeusingnamespacestd;boolcheck(int,int);intmain()inta,b;coutinputa,bab;if(check(a,b)=true)couttrueendl;elsecoutfalseendl;return0;boolcheck(intn,intd)while(n)inte=n%10;n/=10;if(e=d)returntrue;returnfalse;例3:

求正整数2和100之间的完全数。

完全数:

因子之和等于它本身的自然数,如6=1+2+3【问题分析】:

首先判断2-100中的任何一个数是否为完全数,然后在主程序中输出该数。

【算法设计】:

由于2-100之间的任何一个数大于一半就不能被本数整数,所以在函数中判断是否为完全数时只须到一半(即i/2)即可。

参考程序:

#includeusingnamespacestd;boolwqx(intn);intmain()inti;for(i=2;i=100;i+)if(wqx(i)printf(%d,i);return0;boolwqx(intn)intx,s=0;for(x=1;x=n/2;x+)if(n%x=0)s=s+x;if(s=n)returntrue;returnfalse;练习一:

求出1-n之间有所有素数之和。

练习二:

编写函数gcd,功能是利用辗转相除法求两个正整数m和n的最大公约数,并由函数值返回。

练习三:

编写一个求两个数约分,求互质数的程序。

第24课:

函数的嵌套和递归一、函数的嵌套函数的嵌套调用指的是一个函数调用另一个函数,而被调用函数又可调用其它函数。

例如,在调用A函数的过程中,可以调用B函数,在调用B函数的过程中,还可以调用C函数当C函数调用结束后,返回到B函数,当B函数调用结束后,再返回到A函数。

这就是函数的嵌套调用过程。

例1:

函数的嵌套应用举例【问题分析】:

主程序中调用函数1,函数1调用函数2,函数2调用函数3。

【算法设计】:

主程序调用无参数的函数p1(),在p1()中函数调用f(i),而函数f(i)中调用函数p2(i),函数逐级返回。

p2(i)返回f(i),f(i)返回到p1(),p1()返回到主函数。

参考程序:

#includeusingnamespacestd;inti=10;voidp2(inti1)i1*=2;couti1=i1endl;intf(inti)p2(i);return(3*i);voidp1()i=f(i);intmain()couti=iendl;p1();couti=i0时,x!

=x*(x-1)!

假设用函数Fac(x)表示x的阶乘,当x=3时,Fac(3)的求解方法可表示为:

Fac(3)=3*fac

(2)=3*2*Fac

(1)=3*2*1*Fac(0)=3*2*1*1=6定义函数:

intfac(intn)如果n=0,则fac=1;如果n0,则继续调用函数fac=n*fac(n-1);返回主程序,打印fac(x)的结果。

参考程序:

#includeusingnamespacestd;intfac(intn);intmain()intx;cinx;coutx!

=fac(x)endl;return0;intfac(intn)if(n=0)return1;elsereturn(n*fac(n-1);/或returnn=0?

1:

n*fac(n-1);/调用函数fac(n-1)递归求(n-1)!

【说明】:

这里出现了一个小东西,三元运算符“?

:

”。

a?

b:

c的含义是:

如果a为真,则表达式的值是b,否则是c。

所以n=0?

1:

n*fac(n-1)很好地表达了刚才的递归定义。

参考程序二(利用全局变量编程如下):

#includeusingnamespacestd;intt;intfac(intx)if(x=0)t=1;elsefac(x-1);t*=x;intmain()intx;cinx;fac(x);coutt0,n0)【问题分析】:

求两个数的最大公约数,可以用枚举因子的方法,从两者中较小的数枚举到能被两个数同时整除且是最大的约数的方法;也可以用辗转相除法或辗转相减法。

【算法设计】:

这里采用递归实现辗转相除算法:

求m除以n的余数;如果余数不为0,则让m=n,n=余数,重复步骤,即调用子程序;如果余数为0,则终止调用子程序;输出此时的n值。

参考程序一(有参函数):

#includeusingnamespacestd;intgcd(int,int);intmain()intm,n;cinmn;coutm=mn=ngcd=gcd(m,n)endl;return0;intgcd(intm,intn)returnm%n=0?

n:

gcd(n,m%n);参考程序二(采用全局变量):

#includeusingnamespacestd;intd;voidgcd(int,int);intmain()inta,b;cinab;gcd(a,b);coutm=an=bgcd=dendl;return0;voidgcd(intx,inty)if(x%y=0)d=y;elsegcd(y,x%y);练习一:

用递归的方法求1+2+3+N的值。

练习二:

用递归函数输出斐波那契数列第n项(n=30)。

0,1,1,2,3,5,8,13练习三:

植树节那天,有五位同学参加了植树活动,他们完成植树的棵数都不相同。

问第一位同学植了多少棵时,他指着旁边的第二位同学说比他多植了两棵;追问第二位同学,他又说比第三位同学多植了两棵;如此追问,都说比另一位同学多植两棵,最后问到第五位同学时,他说自己植了10棵。

问第一位同学到底植了多少棵树?

第24课:

函数的递归例1:

输入一串以“!

”结束的字符,按逆序输出。

【问题分析】:

可以用循环倒序输出即可,用递归则更简单。

【算法设计】:

递归的本质就是先进再出,且为后进的先计算,所以很简单的就能做到逆序输出。

参考程序:

#includeusingnamespacestd;voidrever()charch;scanf(%c,&ch);if(ch!

=!

)rever();printf(%c,ch);intmain()rever();return0;注意:

通过本题,同学们要再次好好理解递归的本质和运行的顺序。

例2:

用递归函数输出斐波那契数列第n项(n=30)。

0,1,1,2,3,5,8,13【问题分析】:

斐波那契数列是一个古老的数学问题,从数字排列的规律来看,后一项等于前两项之和。

第一个数字0可以看做第零项,也可以看做第一项。

【算法设计】:

设要求的项用x1变量来表示,要求得后一项,前两项必须已知。

参考程序:

#includeusingnamespacestd;intfacci(intn);intmain()intx1;printf(Inputx1:

);scanf(%d,&x1);printf(%d,facci(x1);return0;intfacci(intn)if(n=0)return0;elseif(n=1)return1;elsereturn(facci(n-1)+facci(n-2);例3:

Hanoi(汉诺塔)问题问题的提出:

Hanoi塔由n个大小不同的圆盘和3根木柱1,2,3组成。

开始时,这n个圆盘由大到小依次套在1柱上,如图所示。

现在要求用最少的移动次数把1柱上n个圆盘按下述规则移到3柱上:

(1)一次只能移一个圆盘;

(2)圆盘只能在3个柱上存放;(3)在移动过程中,不允许大盘压小盘。

请编程描述移动的过程。

【问

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 英语

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1