+参考书: 郑莉(清华大学)C++ 语言程序设计 (第3版)
2 函数
较复杂的系统通常需要划分为若干个子系统分别独立的开发和调试。函数就是C/C++中子系统的一种体现。
调用其他函数的函数称为主调函数,被其他函数调用的函数称为被调函数。
2.1 函数的定义和使用
定义
类型标识符 函数名(形式参数表)
{
语句序列
}
类型标识符规定了函数的类型,即返回值的类型,返回值需要使用return
语句;无返回值的函数类型标识符为void
,且不必写return
语句。
形式参数表的通常形式为type1 name1, type2 name2,...,typen namen
调用
-
调用之前需要在主调函数或所有函数之前,如下声明
类型说明符 被调函数名(含类型说明的参数表)
-
调用函数格式如下
函数名(实参列表)
实参列表应与函数原型对应。
例子
//2_1a.cpp #include <iostream> using namespace std; double power(double x, int n); int main() { cout << " 5 to the power 2 is " << power(5,2) << endl; return 0; } double power(double x, int n) { double val = 1.0; while (n--) val *= x; return(val); }
编译运行结果
5 to the power 2 is 25
2.2 函数调用的执行过程
对于操作系统而言,程序编译后生成可执行代码,存放在外部存储器中。程序启动时,系统将可执行代码装载到内存上并开辟一个进程,进程入口就是主函数。当需要调用其他函数时,系统暂停当前函数的执行,保存现场并保留下一条指令的地址,转到子函数入口执行子函数,子函数执行完毕,系统恢复主调函数现场并通过保存的地址找到下一条指令。
2.3 嵌套调用与递归调用
嵌套调用:被调函数还可以作为主调函数调用其他函数,例如
//2_3.cpp
#include <iostream>
using namespace std;
int main()
{
int a,b;
int fun1(int x, int y);
cin >> a >> b;
cout << "a, b的平方和: " << fun1(a, b) << endl;
}
int fun1(int x, int y)
{
int fun2(int m);
return (fun2(x)+fun2(y));
}
int fun2(int m)
{
return (m*m);
}
编译运行结果
1
1
a, b的平方和: 2
递归调用:函数可以直接间接的调用自身,,典型的例子是求阶乘算法。
2.4 参数传递
值调用:发送函数调用时,给形参分配独立的内存空间;单向传递过程,一旦形参获得了值便与 实参脱离关系,此后形参的变化不会影响到实参。
引用调用:引用是一种特殊类型的变量,可以认为是变量的另一个别名。通过引用名与通过被引用的变量名访问变量的效果是一样的
int i,j;
int &ri = i;
j = 10;
ri = j; //相当于 i = j;
- 声明引用时,必须同时对它进行初始化,使它指向一个已经存在的对象;
- 一旦一个引用被初始化后,就不能改为指向其他对象
// 2_4.cpp
#include <iostream>
using namespace std;
void Swap(int& a, int& b);
int main()
{
int x(5), y(10);
cout << "x=" << x << " y=" << y << endl;
Swap(x, y);
cout << "x=" << x << " y=" << y << endl;
return 0;
}
void Swap(int& a, int& b)
{
int t;
t = a;
a = b;
b = t;
}
执行结果
x=5 y=10
x=10 y=5
2.5 内联函数
2.2节中讨论过,当调用函数时,进程需要保存现场和返回地址,返回主调函数还需要恢复现场;这一切都需要时间和空间方面的开销。对于功能简单,规模较小而又使用频繁的函数,这种额外的开销对于程序执行效率的影响很明显;于是设计了内联函数,并规定
-
定义时使用关键字
inline
inline 类型说明符 被调函数名 (含类型说明的形参列表) {函数体语句;}
-
内敛函数体内一般不能有循环语句和
switch
语句 -
内联函数的定义必须在第一次调用之前
-
对内联函数不能进行异常接口声明
-
内联函数不能太过复杂,否则会被编译器转换为普通函数来处理;
主调函数调研内联函数时,并不像调用真正的函数那样;而是将内联函数的函数体直接复制到当前位置,于是就避免了保存现场的开销。
//2_5.cpp
#include <iostream>
using namespace std;
#define PI 3.14
inline double CalArea(double radius)
{
return PI*radius*radius;
}
int main()
{
double r(3.0);
double area;
area = CalArea(r);
cout << area << endl;
}
执行结果
28.26
2.6 带默认形参值的函数
函数定义时可以带有默认的形式参数,调用函数时,若给出实参,则使用实参初始化形参;若没给出实参,则使用预先默认的形式参数。
-
默认形参必须按从右向左的顺序声明,在有默认值的形参的右边,不能有无默认值的形参;
int add(int a, int y = 5, int z = 6); //正确 int add(int a = 1, int y, int z = 6); //错误 int add(int a = 5, int y = 6, int z); //错误
-
相同的作用域内,默认形参的说明应保持唯一;
-
在不同的作用域,允许不同的默认形参说明;
-
默认参数可能给函数重载带来麻烦;
默认参数例子
//2_6.cpp
#include <iostream>
#include <iomanip>
using namespace std;
int get_volume(int length, int width = 2, int height = 3);
int main()
{
int x = 10, y = 12, z = 15;
cout << "Some box data is " << endl;
cout << get_volume(x,y,z) << endl;
cout << get_volume(x,y) << endl;
cout << get_volume(x) << endl;
cout << get_volume(x,7) << endl;
cout << get_volume(5,5,5) << endl;
return 0;
}
int get_volume(int length, int width, int height)
{
cout << setw(5) << length << setw(5) << width << setw(5) << height << ' ';
return length*width*height;
}
执行
Some box data is
10 12 15 1800
10 12 3 360
10 2 3 60
10 7 3 210
5 5 5 125
2.7 函数重载
两个以上的函数,具有相同的函数名,但是形参的个数或者类型不同,编译器会按照实参和形参的类型和个数的最佳匹配,自动调用一个函数,即函数重载。
-
形参必须不同,个数或类型不同
int add(int x, int y); // 正确 float add(float x, float y); // 形参类型不同 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); // 编译器不以返回值区分函数
-
不要将不同功能的函数定义为重载函数,避免对调用结果的误解、混淆;
2.8 C++ 系统函数
一些常用的函数,如sin()
,cos()
,sqrt
等,非常通用;自己编写这些函数费时费力,代码质量不高,通用性也是问题,于是将这些常用函数汇集成一个个的C++库,包括函数库、类库等。
使用函数需要声明函数原型,这些原型在相应的头文件中,因此使用这些函数需要先通过include
指令嵌入相应的头文件(函数声明)。以使用数学函数为例,需要嵌入头文件cmath
//2_8.cpp
#include <iostream>
#include <cmath>
using namespace std;
const double pi(3.14159265);
int main()
{
double a,b;
cin >> a;
b = a*pi/180;
cout << "sin(" << a << ") = " << sin(b) << endl;
cout << "cos(" << a << ") = " << cos(b) << endl;
cout << "tan(" << a << ") = " << tan(b) << endl;
return 0;
}
执行
45
sin(45) = 0.707107
cos(45) = 0.707107
tan(45) = 1
利用系统函数可以大大减少编程的工作量,提高程序运行效率和可靠性;使用中需要注意
- 了解函数库的内容和相应函数的声明、功能;
- 了解函数声明在哪个头文件中;