第3章 函数

C++语言有两种程序模块:函数(function)和类(class)。任何 C++的应用程序都是由各种标准库提供的模块和程序员定义的模块组装而成的。

函数是功能的抽象。所谓功能抽象,是指这个程序模块定义的操作,适用于指定数据类型的数据集。调用者只关心函数能做什么,而不需要关心它是如何做的。函数有两个重要作用:一是任务划分,即把一个复杂任务划分为若干个简单的小任务,便于分工和处理,也便于验证程序的正确性;二是软件重用,即把一些功能相同或相近的程序段,独立编写成函数,让应用程序随时调用,而不需要编写雷同的代码。

函数是程序设计的重要工具。这一章主要介绍函数的定义和调用、函数参数的传递,以及C++程序的结构、变量和函数的作用域、条件编译等有关内容。

有关类的知识,将在第6章之后讨论。

3.1 函数的定义与调用

函数定义由两部分组成:函数首部和函数操作描述。函数首部是函数的接口,包括函数名、函数的参数和返回值类型。函数操作描述由函数体的语句序列实现。

使用函数称为调用函数。函数调用就是通过表达式或语句激活并执行函数代码的过程。函数调用的形式必须与函数定义的接口对应。

3.1.1 函数定义

从用户使用的角度来看,C++有两种函数:标准库函数和用户自定义的函数。

标准库函数由C++系统定义并提供给用户使用,可以看做对语言功能的扩充。例如,fabs函数、get函数等都是标准库函数。

用户根据特定任务编写的函数称为自定义函数。自定义函数的形式与主函数的形式相似,一般形式为:

            类型  函数名([ 形式参数表 ])
            {
                语句序列
            }

函数定义的第一行(可以分多行写)是函数首部(或称函数头),以大括号相括的语句序列为函数体。

其中,“函数名”是用户自定义标识符。“类型”是函数返回表达式的值的类型,简称为返回类型,可以是各种基本类型、结构类型或类类型。若无返回值,则使用空类型符 void。“形式参数表”是用逗号分隔的参数说明列表。省略形式参数时不能省略圆括号,它是函数的识别符号。“函数体”中的语句序列可以包含各种合法C++语句。

形式参数表的一般形式为:

            类型 参数1,类型 参数2,…,类型 参数n

参数是函数与外部传输数据的纽带。若函数的定义省略参数表,则称为无参函数;否则称为有参函数。

无参函数表示函数不依赖外部数据,执行独立的操作。

【例3-1】定义一个无参函数,输出问候语句。

            void printmessage()
            {  cout<<"How do you do!"<<endl;  }

【例3-2】定义一个函数,求两个浮点数之中的大值。函数通过参数从外部接收两个浮点型数据,函数体中用return语句返回结果值。

            double max(double x, double y)
            {  if(x>y)   return x;
              else   return y;
            }

如果一个函数没有返回表达式值,通常说这个函数没有返回值,函数返回类型用void,即函数体内的return语句没带表达式,或可以省略return语句。函数没有返回值不等于不能接收或修改外部数据,在3.2节中将看到,参数是函数与外部传递数据的重要纽带。

3.1.2 函数调用

函数调用要做两件事情:指定函数地址,提供实际参数。函数名是函数的地址,实际参数提供被调用函数执行任务所需要的信息及接收被调用函数返回的信息。

函数调用的一般形式为:

            函数名([实际参数表])

其中,“实际参数表”中的各参数用逗号分隔,实际参数与被调用函数的形式参数在个数、类型、位置上必须一一对应。

不管函数定义是否有参数或者是否有返回值,都可以用两种形式调用:函数语句或函数表达式。

(1)函数语句

函数调用可以作为一个语句。例如,在以下主函数中,用语句调用例3-1定义的函数:

            int main()
            {  printmessage();  }

(2)函数表达式

函数可以通过return语句返回一个结果值。如果定义了这种具有返回结果值的函数,并且调用时需要使用函数的返回值,可以用表达式形式调用函数。

例如,以下两种形式都可以调用例3-2定义的max函数:

            m1 = max(a, b);
            cout << max(m1, c) << endl;

3.1.3 函数原型

函数原型是C++的重要特性之一。函数原型是函数的声明,作用是告诉编译器有关函数接口的信息:函数的名字、函数返回值的数据类型、函数的参数个数、参数类型和参数的顺序,编译器根据函数原型检查函数调用的正确性。

例如,例3-2定义的max函数原型为:

            double max(double, double);

表示max函数有两个double类型参数,返回结果值为double类型。函数原型是一个声明语句,由函数首部加上分号组成。由于函数原型没有实现代码,因此不需要参数名。通常添加参数名是为了增加可读性,但编译器将忽略这些名称。例如:

            double max(double x, double y);
            double max(double a, double b);

是相同的函数原型。

【例3-3】定义和调用max函数。

            #include<iostream>
            using namespace std;
            double max(double,double);            //声明函数原型
            int main()
            {  double a,b,c,m1,m2;
              cout << "input a,b,c:\n";
              cin >> a >> b >> c;
              m1=max(a,b);                    //调用函数
              m2=max(m1,c);                   //调用函数
              cout << "Maximum = " << m2 << endl;
            }
            double max(double x,double y)     //定义函数
            {  if(x>y)  return x;
              else   return y;
            }

如果函数定义出现在程序第一次调用之前,则不需要函数原型声明。这时,函数定义就具有函数原型的作用。例3-3程序的不需要函数原型声明的版本为:

            #include<iostream>
            using namespace std;
            double max(double x,double y)     //定义函数
            {  if(x>y)   return x;
              else   return y;
            }
            int main()
            {  double a,b,c,m1,m2;
              cout<<"input a,b,c:\n";
              cin>>a>>b>>c;
              m1=max(a,b);                    //调用函数
              m2=max(m1,c);                   //调用函数
              cout<<"Maximum = "<<m2<<endl;
            }

标准库函数的函数原型存放在指定的头文件中,用 include 预处理指令获取(具体请参阅附录B)。表3.1列出了cmath头文件中一些常用的数学函数原型。

表3.1 cmath中几个常用的数学函数原型

【例3-4】求正弦和余弦值。

            #include<iostream>
            #include<cmath>
            using namespace std;
            int main()
            {  double PI=3.1415926535;
              double x, y;
              x = PI/2;
              y=sin(x);      //调用标准函数
              cout<<"sin("<<x<<")="<<y<<endl;
              y=cos(x);      //调用标准函数
              cout<<"cos("<<x<<")="<<y<<endl;
            }

程序运行结果:

            sin(1.5708) = 1
            cos(1.5708) = 4.48966e-011

第2行输出显示了0的近似值。