[其它课程]第三章 函数.ppt
第三章 函 数,本章主要内容,函数的定义和调用函数间的参数传递内联函数带默认形参值的函数函数重载C+系统函数,一、函数的定义和调用,函数定义的语法形式 类型标识符 函数名(形式参数表)语句序列,是被初始化的内部变量,寿命和可见性仅限于函数内部,若无返回值,写void,函数的定义,形式参数表 name1,name2,.,namen函数的返回值由 return 语句给出,例如:return 0无返回值的函数(void类型),不必写return 语句。,形参(形式参数)在定义函数时,函数名后面括号中的变量名称为“形参”实参(实际参数)主函数总调用一个函数时,函数名后面括号中的参数(也可以是一个表达式)称为“实参”。,函数的调用,调用前先在主调函数中声明函数原型它保持若函数定义在调用点之前,则无需另外声明;若函数定义在调用点之后,则需要在调用函数前按如下形式声明函数原型:类型标识符 被调用函数名(含类型说明的形参表);,调用形式 函数名(实参列表)嵌套调用 函数可以嵌套调用,但不允许嵌套定义。递归调用 函数直接或间接调用自身。,#include using namespace std;/计算x的n次方double power(double x,int n)double val=1.0;while(n-)val*=x;return val;/函数的返回值;int main()cout 5 to the power 2 is power(5,2)endl;return 0;,例 编写一个求x的n次方的函数,运行结果:5 to the power 2 is 25,例如:11012=1(23)+1(22)+0(21)+1(20)=1310 如果输入1101,则应输出13,例 输入一个8位二进制数,将其转换为十进制数输出。,#include using namespace std;/计算x的n次方double power(double x,int n);/声明函数原型int main()int i;int value=0;char ch;,cout=0;i-)cin ch;if(ch=1)value+=int(power(2,i);cout Decimal value is value endl;return 0;double power(double x,int n)double val=1.0;while(n-)val*=x;return val;,运行结果:Enter an 8 bit binary number 01101001Decimal value is 105,例 编写程序求的值,其中arctan用如下形式的级数计算:,直到级数某项绝对值不大于10-15为止;和x均为double型。,#include using namespace std;double arctan(double x)/函数定义在主函数之前,可以不必加以声明。double sqr=x*x;double e=x;double r=0;int i=1;while(e/i 1e-15)double f=e/i;r=(i%4=1)?r+f:r-f;e=e*sqr;i+=2;return r;,int main()double a=16.0*arctan(1/5.0);double b=4.0*arctan(1/239.0);/注意:因为整数相除结果取整,如果参数写1/5,1/239,结果就都是0cout PI=a-b endl;return 0;,运行结果:PI=3.14159,说明:回文各位数字左右对称的整数。例如:11满足上述条件 112=121,113=1331。分析:采用除以10取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数为回文。,例 寻找并输出11-999之间的数m,它满足m、m2和m3均为回文数。,#include using namespace std;/判断n是否为回文数bool symm(unsigned n)unsigned i=n;unsigned m=0;while(i 0)m=m*10+i%10;i/=10;return m=n;,17,int main()for(unsigned m=11;m 1000;m+)if(symm(m),运行结果:m=11 m*m=121 m*m*m=1331m=101 m*m=10201 m*m*m=1030301m=111 m*m=12321 m*m*m=1367631,例 计算如下公式,并输出结果:,其中r、s的值由键盘输入。sin x的近似值按如下公式计算,计算精度为10-6:,#include#include/对C+标准库中数学函数的说明using namespace std;const double TINY_VALUE=1e-10;double tsin(double x)double g=0;double t=x;int n=1;do g+=t;n+;t=-t*x*x/(2*n-1)/(2*n-2);while(fabs(t)=TINY_VALUE);return g;,int main()double k,r,s;cout r;cout s;if(r*r=s*s)k=sqrt(tsin(r)*tsin(r)+tsin(s)*tsin(s);elsek=tsin(r*s)/2;cout k endl;return 0;,22,运行结果:r=5s=81.37781,24,每个骰子有六面,点数分别为1、2、3、4、5、6。游戏者在程序开始时输入一个无符号整数,作为产生随机数的种子。每轮投两次骰子,第一轮如果和数为7或11则为胜,游戏结束;和数为2、3或12则为负,游戏结束;和数为其它值则将此值作为自己的点数,继续第二轮、第三轮.直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。由rolldice函数负责模拟投骰子、计算和数并输出和数。,例 投骰子的随机游戏,25,rand函数原型:int rand(void);所需头文件:功能和返回值:求出并返回一个伪随机数 srand函数原型:void srand(unsigned int seed);参数:seed产生随机数的种子。所需头文件:功能:为使rand()产生一序列伪随机整数而设置起始点。使用1作为seed参数,可以重新初化rand()。,#include#include using namespace std;/投骰子、计算和数、输出和数int rollDice()int die1=1+rand()%6;int die2=1+rand()%6;int sum=die1+die2;cout player rolled die1+die2=sum endl;return sum;,26,enum GameStatus WIN,LOSE,PLAYING;int main()int sum,myPoint;GameStatus status;unsigned seed;cout seed;/输入随机数种子srand(seed);/将种子传递给rand()sum=rollDice();/第一轮投骰子、计算和数,27,switch(sum)case 7:/如果和数为7或11则为胜,状态为WINcase 11:status=WIN;break;case 2:/和数为2、3或12则为负,状态为LOSEcase 3:case 12:status=LOSE;break;default:/其它情况,游戏尚无结果,状态为PLAYING,记下点数,为下一轮做准备 status=PLAYING;myPoint=sum;cout point is myPoint endl;break;,28,while(status=PLAYING)/只要状态仍为PLAYING,就继续进行下一轮 sum=rollDice();if(sum=myPoint)/某轮的和数等于点数则取胜 status=WIN;else if(sum=7)/出现和数为7则为负 status=LOSE;/当状态不为PLAYING时上面的循环结束,以下程序段输出游戏结果if(status=WIN)cout player wins endl;else cout player loses endl;return 0;,29,运行结果:Please enter an unsigned integer:23player rolled 6+3=9point is 9player rolled 5+4=9player wins,30,函数声明的意义,31,函数原型是主调函数与被调函数间的协议,函数的原型信息(参数个数和类型、返回值 类型)在编译后即不存在;如果不要求声明函数,以错误的方式(错误 的参数数量或类型)调用函数,会产生不可 预期的结果,但很多情况下不会给出错误提 示。,32,main调fun1()结束,fun1()调fun2()返回,fun2()返回,函数的调用之嵌套调用,33,#include using namespace std;int fun2(int m)return m*m;int fun1(int x,int y)return fun2(x)+fun2(y);,例 输入两个整数,求平方和。,int main()int a,b;cout a b;cout The sum of square of a and b:fun1(a,b)endl;return 0;,34,运行结果:Please enter two integers(a and b):3 4The sum of square of a and b:25,35,函数的调用之递归调用,函数直接或间接地调用自身,称为递归调用。递归过程的两个阶段:递推:4!=43!3!=32!2!=21!1!=10!0!=1未知 已知回归:4!=43!=243!=32!=62!=21!=21!=10!=10!=1 未知 已知,36,分析:计算n!的公式如下:这是一个递归形式的公式,应该用递归函数实现。,例 求n!,#include using namespace std;unsigned fac(int n)unsigned f;if(n=0)f=1;else f=fac(n-1)*n;return f;,37,int main()unsigned n;cout n;unsigned y=fac(n);cout n!=y endl;return 0;运行结果:Enter a positive integer:88!=40320,38,39,分析:由n个人里选k个人的组合数=由n-1个人里选k个人的组合数+由n-1个人里选k-1个人的组合数当n=k或k=0时,组合数为1,例 用递归法计算从n个人中选择k个人组成一个委员会的不同组合数。,#include using namespace std;int comm(int n,int k)if(k n)return 0;else if(n=k|k=0)return 1;else return comm(n-1,k)+comm(n-1,k-1);,40,int main()int comm(int n,int k);int n,k;cout n k;cout C(n,k)=comm(n,k)endl;return 0;,运行结果:18 58568,42,有三根针A、B、C。A针上有N个盘子,大的在下,小的在上,要求把这N个盘子从A针移到C针,在移动过程中可以借助B针,每次只允许移动一个盘,且在移动过程中在三根针上都保持大盘在下,小盘在上。,例 汉诺塔问题,分析:将n 个盘子从A针移到C针可以分解为下面三个步骤:将A 上n-1个盘子移到 B针上(借助C针);把A针上剩下的一个盘子移到C针上;将n-1个盘子从B针移到C针上(借助A针);事实上,上面三个步骤包含两种操作:将多个盘子从一个针移到另一个针上,这是一个递归的过程。hanoi函数实现。将1个盘子从一个针上移到另一针上。用move函数实现。,43,#include using namespace std;/把src针的最上面一个盘子移动到dest针上void move(char src,char dest)cout dest endl;/把n个盘子从src针移动到dest针,以medium针作为中介void hanoi(int n,char src,char medium,char dest)if(n=1)move(src,dest);,44,else hanoi(n-1,src,dest,medium);move(src,dest);hanoi(n-1,medium,src,dest);int main()int m;cout m;cout the steps to moving m diskes:endl;hanoi(m,A,B,C);return 0;,45,运行结果:Enter the number of diskes:3the steps to moving 3 diskes:A-CA-BC-BA-CB-AB-CA-C,46,47,二、函数的参数传递机制,在函数被调用时才分配形参的存储单元。实参可以是常量、变量或表达式。实参类型必须与形参相符。传递时是传递参数值,即单向传递。,48,函数的参数传递举例,49,#includeusing namespace std;void swap(int a,int b)int t=a;a=b;b=t;,例 输入两个整数交换后输出,int main()int x=5,y=10;cout x=x y=y endl;swap(x,y);cout x=x y=y endl;return 0;运行结果:x=5 y=10 x=5 y=10,51,52,函数的引用调用,引用(,声明一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象。一旦一个引用被初始化后,就不能改为指向其它对象。引用可以作为形参 void swap(int&a,int&b).,54,#includeusing namespace std;void swap(int,例 输入两个整数交换后输出,swap(x,y);,55,运行结果:x=5 y=10 x=10 y=5,57,注意:内联函数体内不能有循环语句和switch语句。内联函数的声明必须出现在内联函数第一次被调用之前。对内联函数不能进行异常接口声明。,三、内联函数声明和使用,编译时在调用处用函数体进行替换,节省 了参数传递、控制转移等开销。声明时使用关键字 inline.,58,#include using namespace std;const double PI=3.14159265358979;inline double calArea(double radius)/内联函数,计算园的面积 return PI*radius*radius;int main()double r=3.0;double area=calArea(r);cout area endl;return 0;,例 内联函数应用举例,59,函数在声明时可以预先给出缺省的形参值,调用时如给出实参,则采用实参值,否则采用预先给出的缺省形参值。例如:int add(int x=5,int y=6)return x+y;int main()add(10,20);/10+20add(10);/10+6add();/5+6,四、缺省形参值的作用,60,有缺省参数的形参必须在形参列表的最后,也就是说缺省形参值的右面不能有无缺省值的参数。因为调用时实参与形参的结合是从左向右的顺序。例:int add(int x,int y=5,int z=6);/正确int add(int x=1,int y=5,int z);/错误int add(int x=1,int y,int z=6);/错误,缺省形参值的说明次序,61,如果一个函数有原型声明,且原型声明在定义之前,则缺省形参值必须在函数原型声明中给出;而如果只有函数的定义,或函数定义在前,则缺省形参值需在函数定义中给出。例:,int add(int x=5,int y=6);/原型声明在前int main()add();int add(int x,int y)/此处不能再指定缺省值 return x+y;,int add(int x=5,int y=6)/只有定义,没有原型声明 return x+y;int main()add();,缺省形参值与函数的调用位置,62,C+允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。例:,五、重载函数的声明,63,注意,不要将不同功能的函数声明为重载函数,以免出现调用结果的误解、混淆:,重载函数的形参必须不同:个数不同或类型不同。编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数。,64,#include using namespace std;int sumOfSquare(int a,int b)return a*a+b*b;double sumOfSquare(double a,double b)return a*a+b*b;,例 编写两个名为sumOfSquare的重载函数,分别求两整数的平方和及两实数的平方和。,int main()int m,n;cout m n;cout x y;cout Their sum of square:sumOfSquare(x,y)endl;return 0;,65,运行结果:Enter two integer:3 5Their sum of square:34Enter two real number:2.3 5.8Their sum of square:38.93,66,67,C+的系统库中提供了几百个函数可供程序员使用。例:求平方根函数(sprt)、求绝对值函数(abs)等。注意:使用系统函数时要包含相应的头文件。例:cmath 或 math.h,六、C+系统函数,68,分析:系统函数中提供了求正弦值、余弦值和正切值的函数:sin()、cos()、tan(),函数的说明在头文件cmath中。,例 从键盘输入一个角度值,求出该角度的正弦值、余弦值和正切值。,#include#include using namespace std;const double PI=3.14159265358979;int main()double angle;cout angle;/输入角度值double radian=angle*PI/180;/转化为弧度值cout sin(angle)=sin(radian)endl;cout cos(angle)=cos(radian)endl;cout tan(angle)=tan(radian)endl;return 0;,69,标准C+函数C+标准中规定的函数;各种编译环境普遍支持,因此用标准函数的程序移植性好;很多标准C+函数继承自标准C,头文件以c开头:cmath,cstdlib,cstdio,ctime非标准C+函数与特定操作系统或编译环境相关;在处理和操作系统相关事务时常常需要调用。,70,标准函数与非标准函数,