C++ 第3章 函数

3.1 函数的定义与使用

3.1.1 函数的定义

函数是面向对象程序设计中,对功能的抽象

类型标识符  函数名(形式参数表)                        
{  
   语句序列
}

3.1.2 函数的调用

  • 调用前先声明函数原型:
    • 类型标识符  被调用函数名 (含类型说明的形参表);
  • 调用形式
    •   函数名(实参列表)
  • 嵌套调用:函数可以嵌套调用,但不允许嵌套定义。
  • 递归调用:函数直接或间接调用自身

例:

#include <iostream>
using namespace std;
//计算x的n次方
double    power (double x, int n);
void main(void)
{
    cout << "5 to the power 2 is "<< power(5,2) << endl;
}

double    power (double x, int n)
{
    double val = 1.0;
    while (n--)
        val = val*x;
    return(val);
}

运行结果:5 to the power 2 is 25

由于函数power 的定义位于调用之后,所以需要先对函数原型加以声明。

例: 输入一个8位二进制数,将其转换为十进制数输出。11012 = 1(23) + 1(22) + 0(21) + 1(20)  = 1310   所以,如果输入1101,则应输出13

#include <iostream>
using namespace std;

//计算x的n次方
double power (double x, int n);

void main(void)
{
    int  i;
    int  value = 0;
    char ch;
    cout << "Enter an 8 bit binary number  ";
    for (i = 7; i >= 0; i--)
    {
        cin >> ch;
        if (ch == '1')
            value += int(power(2,i));
    }
    cout <<"Decimal value is  "<<value<<endl;
}

double power (double x, int n)
{
    double val = 1.0;
    while (n--)  val *= x;
    return(val);
}

例:其中arctan用如下形式的级数计算: 直到级数某项绝对值不大于10-15为止;π和x均为double型。

#include<iostream>
using namespace std;

void main()
{
  double a,b;
  double arctan(double x)  ; //函数原型声明

  a=16.0*arctan(1/5.0)  ; 
  b=4.0*arctan(1/239.0)  ; 
    //注意:因为整数相除结果取整,
    //如果参数写1/5,1/239,结果就都是0
  cout<<"PI="<<a-b<<endl;
}

double arctan(double x)
{  int i;
  double r,e,f,sqr;
  sqr=x*x;
  r=0;    e=x;    i=1;
  while(e/i>1e-15)
  {
    f=e/i;
    r=(i%4==1)? r+f : r-f   ;
    e=e*sqr;      i+=2;
  }
  return  r ;
}

运行结果:   PI=3.14159


例:寻找并输出11~999之间的数m,它满足m、m2和m3均为回文数。
回文:各位数字左右对称的整数。 例如:11满足上述条件     112=121,113=1331。
分析: 10取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数为回文。

#include <iostream>
using namespace std;

void main()
{
  bool symm(long n);

  long m;

  for(m=11; m<1000; m++)
    if (symm(m)&&symm(m*m)&&symm(m*m*m)){ 
        cout<<"m="<<m;
        cout<<"m*m="<<m*m;
        cout<<<<"m*m*m="<<m*m*m<<endl;
    }
}

bool symm(long n)
{
  long i, m;
  i=n ;  
  m=0 ;
  while(i>0){
   m=m*10+i%10;
   i=i/10;
  }
  return m==n;
}

运行结果:

m=11  m*m=121  m*m*m=1331
m=101  m*m=10201  m*m*m=1030301
m=111  m*m=12321  m*m*m=1367631

例:计算如下公式,并输出结果。

 其中r 、s的值由键盘输入。sinx 的近似值按如下公式计算:

计算精度为10-6,当某项的绝对值小于计算精度时,停止累加,累加和即为该精度下的sinx的近似值

#include <iostream>
#include<cmath>//头文件cmath中具有对C++标准库中数学函数的说明
using namespace std;

void main()
{
  double k,r,s;
  double tsin(double x);

  cout<<"r=";
  cin>>r;
  cout<<"s=";
  cin>>s;
  if (r*r<=s*s)
    k=sqrt(tsin(r)*tsin(r)+tsin(s)*tsin(s))  ;
  else
    k=tsin(r*s)/2;
  cout<<k<<endl;
}

double tsin(double x)
{
  double p=0.000001,g=0,t=x;
  int n=1;
  do {
     g=g+t;
     n++;
     t=-t*x*x/(2*n-1)/(2*n-2);
  }while(fabs(t)>=p); 
  return g;
}

例:投骰子的随机游戏
游戏规则是:每个骰子有六面,点数分别为1、2、3、4、5、6。游戏者在程序开始时输入一个无符号整数,作为产生随机数的种子。
每轮投两次骰子,第一轮如果和数为7或11则为胜,游戏结束;和数为2、3或12则为负,游戏结束;和数为其它值则将此值作为自己的点数,继续第二轮、第三轮...直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。
由rolldice函数负责模拟投骰子、计算和数并输出和数。

#include <iostream>
#include <cstdlib>
using namespace std;

int rolldice(void)
{ //投骰子、计算和数、输出和数
  int die1,die2,worksum;
  die1=1+rand()%6;
  die2=1+rand()%6;
  worksum=die1+die2;
  cout<<"player rolled"<<die1<<'+'<<die2<<'='<<worksum<<endl;
  return worksum;
}

void main()
{
  int gamestatus,sum,mypoint;
  unsigned seed; 
  cout<<"Please enter an unsigned integer:";
  cin>>seed;          //输入随机数种子
  srand(seed);       //将种子传递给rand()
  sum=rolldice();   //第一轮投骰子、计算和数

  switch(sum)
    {
      case 7:   //如果和数为7或11则为胜,状态为1
      case 11:   gamestatus=1;
                        break;
      case 2:   //和数为2、3或12则为负,状态为2
      case 3: 
      case 12:   gamestatus=2;
                       break;
      default:   //其它情况,游戏尚无结果,状态为0,记下点数,为下一轮做准备
             gamestatus=0;
             mypoint=sum  ;
             cout<<"point is "<<mypoint<<endl;
             break;
    }

  while (  gamestatus==0  )  //只要状态仍为 0,就继续进行下一轮
    {
      sum=rolldice();
      if(sum==mypoint)    //某轮的和数等于点数则取胜,状态置为1
        gamestatus=1  ;
      else
        if (  sum==7  )    //出现和数为7则为负,状态置为2
          gamestatus=2;
    }
  //当状态不为0时上面的循环结束,以下程序段输出游戏结果
    if(  gamestatus==1  )
      cout<<"player wins\n";
    else
      cout<<"player loses\n";

}

2、嵌套调用
例:输入两个证书,求他们的平方和

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int fun1(int x,int y)
 5 {
 6    int fun2(int m);
 7    return (fun2(x)+fun2(y));
 8 }
 9 
10 int fun2(int m)
11 {
12    return (m*m);
13 }
14 
15 void main(void)
16 {
17    int a,b;
18    int fun1(int x,int y);
19    cin>>a>>b;
20    cout<<"a、b的平方和:"              <<fun1(a,b)<<endl;
21 }

3、递归调用
函数可以直接或间接地调用自身,称为递归调用。
递归过程的两个阶段:

  • 递推:  4!=4×3! → 3!=3×2! →  2!=2×1!  → 1!=1×0!   →   0!=1
  • 未知 →     已知
  • 回归: 4!=4×3!=24  ←   3!=3×2!=6   ←   2!=2×1!=2   ←   1!=1×0!=1   ←   0!=1
  • 未知 ← 已知

例:求n!
分析:计算n!的公式如下:

 1 #include <iostream>
 2 using namespace std;
 3 long fac(int n)
 4 {
 5   long f;
 6   if (n<0) 
 7       cout<<"n<0,data error!"<<endl;
 8   else if (n==0) f=1;
 9   else f=fac(n-1)*n;
10   return(f);
11 }
12 void main()
13 {
14   long fac(int n);
15   int n;
16   long y;
17   cout<<"Enter a positive integer:";
18   cin>>n;
19   y=fac(n);
20   cout<<n<<"!="<<y<<endl;
21 }

运行结果:
Enter a positive integer:8
8!=40320

例:用递归法计算从n 个人中选择k个人组成一个委员会的不同组合数。
分析: 由n个人里选k个人的组合数  =  由n-1个人里选k个人的组合数    +   由n-1个人里选k-1个人的组合数
当n==k或k==0时,组合数为1

 1 #include<iostream>
 2 using namespace std;
 3 void main()
 4 {   int n,k;
 5    int comm(int n, int k);
 6    cin>>n>>k;
 7    cout<<comm(n,k) <<endl;
 8 }
 9 int comm(int n, int k)
10 {   if (  k>n  )     return 0;
11    else if(  n==k||k==0  )
12      return 1;
13    else
14      return  comm(n-1,k)+comm(n-1,k-1)  ;
15 }
 

  例:汉诺塔问题:有三根针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函数实现。

 1 #include <iostream>
 2 using namespace std;
 3 void move(char getone,char putone)
 4 {     cout<< getone <<"-->"<<putone<<endl; }
 5 void hanoi(int n,char one,char two,char three)
 6 {    void move(char getone,char putone);
 7     if (n==1) move (one,three);
 8     else
 9     {   hanoi (n-1,one,three,two);
10         move(one,three);
11         hanoi(n-1,two,one,three);
12     }
13 }
14 void main()
15 {
16     void hanoi(int n,char one,char two,char three);
17     int m;
18     cout<<"Enter the number of diskes:";
19     cin>>m;
20     cout<<"the steps to moving "<<m<<" diskes:"<<endl;
21     hanoi(m,'A','B','C');
22 }
 

运行结果:
Enter the number of diskes:3
the steps to moving 3 diskes:
A-->C
A-->B
C-->B
A-->C
B-->A
B-->C
A-->C

3.1.3 函数的参数传递

在函数被调用时才分配形参的存储单元。
1、值传递
例:输入两整数交换后输出

#include<iostream>
using namespace std;

void Swap(int a, int b)
{
    int t;
    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
分析:从上面的运行结果可以看出,并没有达到交换的目的。这是因为,采用的是值传递,函数调用时传递的是实参的值,是单向传递过程。形参值的改变对实参不起作用。
2、形参传递
引用(&)是一种特殊类型的变量,可以被认为是另一个变量的别名

int i,j;
int &ri=i; //建立一个int型的引用ri,并将其初始化为变量i的一个别名
j=10;
ri=j;//相当于 i=j;

使用引用时必须注意下列问题:

  • 声明一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象。
  • 一旦一个引用被初始化后,就不能改为指向其它对象。

用引用作为形参,在函数调用时发生的参数传递,称为引用传递。
例:输入两个整数交换后输出

 1 #include<iostream>
 2 using namespace std;
 3 void Swap(int& a, int& b);
 4 int main()
 5 {    int x(5), y(10);
 6     cout<<"x="<<x<<"    y="<<y<<endl;
 7     Swap(x,y);
 8     cout<<"x="<<x<<"    y="<<y<<endl;
 9     return 0;
10 }
11 void Swap(int& a, int& b)
12 {    int t;
13     t=a;
14     a=b;
15     b=t;
16 }

运行结果:
x=5      y=10
x=10      y=5

3.2内联函数

声明时使用关键字 inline
对于一些功能简单、规模较小又使用频繁的函数,可以设计为内联函数。
内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。这样就节省了参数传递、控制转移等开销。
注意:

  • 内联函数体内不能有循环语句和switch语句。
  • 内联函数的声明必须出现在内联函数第一次被调用之前。
  • 对内联函数不能进行异常接口声明。

例:

 1 #include<iostream>
 2 using namespace std;
 3 inline double CalArea(double radius)
 4 {    return 3.14*radius*radius;
 5 }
 6 int main()
 7 {
 8     double r(3.0);
 9     double area;
10     area=CalArea(r);
11     cout<<area<<endl;
12     return 0;
13 }

3.3 带默认形参值得函数

函数在定义时可以预先声明默认的形参值。调用时如果给出实参,则用实参初始化形参,如果没有给出实参,则采用预先声明的默认形参值。

int add(int x=5,int y=6) //声明默认形参值 { return x+y; } void main(void) { add(10,20); //10+20 add(10); //10+6 add(); //5+6 } 

默认形参值的说明次序:
默认形参值必须从右向左顺序声明,并且在默认形参值的右面不能有非默认形参值的参数。因为调用时实参取代形参是从左向右的顺序。例如:

例:
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); //错误 

默认形参值与函数的调用位置:
如果一个函数在定义之前又有原型声明,默认形参值需要在原型声明中给出,定义中不能再出现默认形参值。

int add(int x=5,int y=6);//默认形参在函数原型中给出 void main(void) { add(); //调用在实现前 } int add(int x,int y) { return x+y; } 

默认形参值的作用域:
在相同的作用域内,默认形参值的说明应保持唯一,但如果在不同的作用域内,允许说明不同的默认形参。

int add(int x=1,int y=2); void main(void) { int add(int x=3,int y=4); add(); //使用局部默认形参值(实现3+4) } void fun(void) { ... add(); //使用全局默认形参值(实现1+2) }

3.4函数的重载

C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。
注意事项:

  • 重载函数的形参必须不同: 个数不同或类型不同。
  • 编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数。
1、形参类型不同
int add(int x,int y);
float add(float x , float y);
2、形参个数不同
int add(int x,int y);
int add(int x,int y,int z);
int add (int x, int y); 
int add (int a , int b) ;
//错误!编译器不以形参名来区分函数

int add (int x, int y); 
void add (int x, int y) ;
//错误!编译器不以返回值来区分函数
  • 不要将不同功能的函数声明为重载函数,以免出现调用结果的误解、混淆。这样不好。
int add(int x , int y) {return x+y;}

float add(float x , float y) {return x-y;}

例子:编写三个名为add的重载函数,分别实现两整数相加、两实数相加和两个复数相加的功能。

 1 #include<iostream>
 2 using namespace std;
 3 struct complex
 4 {
 5     double real;
 6     double imaginary;
 7 };
 8 
 9 void main(void)
10 {
11     int m, n;
12     double x, y;
13     complex c1, c2, c3;
14     int add(int m, int n);
15     double add(double x, double y);
16     complex add(complex c1, complex c2);
17 
18     cout<<"Enter two integer: ";
19     cin>>m>>n;
20     cout<<"integer "<<m<<'+'<<n<<"="<<add(m,n)<<endl;
21 cout<<"Enter two real number: ";
22     cin>>x>>y;
23     cout<<"real number "<<x<<'+'<<y<<"= "<<add(x,y)
24             <<endl;
25 
26     cout<<"Enter the first complex number: ";
27     cin>>c1.real>>c1.imaginary;
28     cout<<"Enter the second complex number: ";
29     cin>>c2.real>>c2.imaginary;
30     c3=add(c1,c2);
31     cout<<"complex number (" <<c1.real<< ','
32            << c1.imaginary <<")+("<<c2.real<<','
33            <<c2.imaginary<<")=("<<c3.real<<','
34            <<c3.imaginary<<")\n";
35 }
36 
37 int add(int m, int n)
38 {  return m+n; }
39 
40 double add(double x, double y)
41 {  return x+y; }
42 
43 complex add(complex c1, complex c2)
44 {
45     complex c;
46     c.real=c1.real+c2.real;
47     c.imaginary=c1.imaginary+c2.imaginary;
48     return c;
49 }

运行结果:
Enter two integer: 3 5
integer 3+5=8
Enter two real number: 2.3 5.8
real number 2.3+5.8= 8.1
Enter the first complex number: 12.3 45.6
Enter the second complex number: 56.7 67.8
complex number (12.3,45.6)+(56.7,67.8)= (69,113.4)

3.5 C++ 系统函数

C++的系统库中提供了几百个函数可供程序员使用。 例如:求平方根函数(sprt)、求绝对值函数(abs)等。
使用系统函数时要包含相应的头文件。 例如:math.h 或 cmath
例:从键盘输入一个角度值,求出该角度的正弦值、余弦值和正切值。
分析: 系统函数中提供了求正弦值、余弦值和正切值的函数:sin()、cos()、tan(),函数的说明在头文件math.h中。

 1 #include<iostream>
 2 #include<cmath>
 3 using namespace std;
 4 const double pi(3.14159265);
 5 void main()
 6 {   double a,b;
 7     cin>>a;
 8     b=a*pi/180;
 9     cout<<"sin("<<a<<")="<<sin(b)<<endl;
10     cout<<"cos("<<a<<")="<<cos(b)<<endl;
11     cout<<"tan("<<a<<")="<<tan(b)<<endl;
12 }

运行结果:
30
sin(30)=0.5
cos(30)=0.866025
tan(30)=0.57735

3.6 深度搜索

3.6.1 运行栈与函数调用的执行

3.6.2 函数声明与类型安全

猜你喜欢

转载自www.cnblogs.com/alec7015/p/12391839.html