第三章 函数
清华大学计算机与信息管理中心
郑 莉
C++语言程序设计
前一页 休息 2
本章主要内容
? 函数的声明和调用
? 函数间的参数传递
? 内联函数
? 带缺省形参值的函数
? 函数重载
? 函数模板
? C++系统函数
前一页 休息 3
函数的声明
? 函数是面向对象程序设计中的基本抽象
单元,是对功能的抽象
? 函数声明的语法形式
类型标识符 函数名(形式参数表)
{
语句序列
}






使

若无参数,写 void
是被初始化的内部
变量,寿命和可见
性仅限于函数内部
若无返回值,写 void
前一页 休息 4
函数的声明
? 形式参数表
<type1> name1,<type2> name2,...,
<typen> namen
? 函数的返回值
– 由 return 语句给出,例如:
return 0
–无返回值的函数( void类型),不必写
return语句。






使

前一页 休息 5
函数的调用
? 调用前先说明函数原型:
– 在调用函数的说明部分,或程序文件开头
所有函数之前,按如下形式说明:
类型标识符 被调用函数名 (含类型说明的形参表) ;
? 调用形式
函数名(实参列表)
? 嵌套调用
– 函数不允许嵌套声明,但可以嵌套调用。
? 递归调用
– 函数直接或间接调用自身。






使

前一页 休息 6
例 3-1编写一个求 x的 n次方的函数
#include <iostream.h>
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);
}






使

前一页 休息 7
运行结果:
5 to the power 2 is 25
例 3-1编写一个求 x的 n次方的函数函





使

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






使

#include <iostream.h>
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);
}
运行结果:
Enter an 8 bit binary number 01101001
Decimal value is 105
前一页 休息 11
例 3-3编写程序求 π的值
其中 arctan用如下形式的级数计算:
直到级数某项绝对值不大于 10-15为止;
π和 x均为 double型 。






使

?
?
??
?
???
?
??
?
??
239
1ar ct an4
5
1ar ct an16?
??????
753
)a r c t a n (
753 xxx
xx
#include<iostream.h>
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
运行结果:
PI=3.14159
前一页 休息 15
例 3-4
? 寻找并输出 11~999之间的数 m,它满足 m、
m2和 m3均为回文数。
– 回文:各位数字左右对称的整数。
例如,11满足上述条件
112=121,113=1331。
? 分析:
– 10取余的方法,从最低位开始,依次取出该数
的各位数字。按反序重新构成新的数,比较与
原数是否相等,若相等,则原数为回文。






使

#include <iostream.h>
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<<" m*m="<<m*m
<<" m*m*m="<<m*m*m<<endl;
}
bool symm(long n)
{
long i,m;
i=n ; m=0 ;
while(i)
{
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
前一页 休息 19
例 3-5
计算如下公式,并输出结果:
其中 r,s的值由键盘输入。 SIN x的近
似值按如下公式计算,计算精度为 10-6:






使

?
?
?
?
?
?
??
?
22
2222
r)*(
2
1
r)()(
ssrS I N
ssS I NrS I N
k


??
?
?
?
???????? 1
12
1
753
)!12()1(!7!5!3!1 n
n
n
n
xxxxxS I Nx ?
#include <iostream.h>
#include<math.h>
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;
}
运行结果:
r=5
s=8
1.37781
前一页 休息 22
例 3-6投骰子的随机游戏
游戏规则是:每个骰子有六面, 点数分别为 1,2、
3,4,5,6。 游戏者在程序开始时输入一个无符号整
数, 作为产生随机数的种子 。
每轮投两次骰子, 第一轮如果和数为 7或 11则为胜,
游戏结束;和数为 2,3或 12则为负, 游戏结束;和数
为其它值则将此值作为自己的点数, 继续第二轮, 第
三轮,..直到某轮的和数等于点数则取胜, 若在此前出
现和数为 7则为负 。
由 rolldice函数负责模拟投骰子, 计算和数并输出
和数 。






使

? rand
函数原型,int rand(void);
所需头文件,<stdlib.h>
功能和返回值:求出并返回一个伪随机数
? srand
函数原型,void srand(unsigned int seed);
参数,seed产生随机数的种子。
所需头文件,<stdlib.h>
功能:为使 rand()产生一序列伪随机整数而设
置起始点。使用 1作为 seed参数,可以重新
初化 rand()。
#include <iostream.h>
#include <stdlib.h>
int rolldice(void);
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";
}
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;
}
运行结果 2:
Please enter an unsigned integer:23
player rolled 6+3=9
point is 9
player rolled 5+4=9
player wins
前一页 休息 29
函数调用的执行过程函





使

main()
调 fun()
结束
fun()
返回
① ②



保存:
返回地址
当前现场

恢复:
主调程序现场
返回地址

前一页 休息 30
嵌套调用函





使

main{}
调 fun1()
结束
fun1()
调 fun2()
返回
fun2()
返回
① ② ③



⑥⑧

前一页 休息 31
例 3-6 输入两个整数,求平方和。
#include <iostream.h>
void main(void)
{
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);
}
运行结果:
3 4
a,b的平方和,25
前一页 休息 33
递归调用
?函数直接或间接地调用自身,称为递归调用。
?递归过程的两个阶段:
–递推:
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
未知 已知






使

前一页 休息 34
例 3-8 求 n!
分析:计算 n!的公式如下:
这是一个递归形式的公式,应该用递
归函数实现。






使

?
?
?
??
?
?
)0()!1(
)0(1
!
nnn
n
n
源程序:
#include <iostream.h>
long fac(int n)
{
long f;
if (n<0)
cout<<"n<0,data error!"<<endl;
else if (n==0) f=1;
else f=fac(n-1)*n;
return(f);
}
void main()
{
long fac(int n);
int n;
long y;
cout<<"Enter a positive integer:";
cin>>n;
y=fac(n);
cout<<n<<"!="<<y<<endl;
}
运行结果:
Enter a positive integer:8
8!=40320
前一页 休息 37
例 3-9
? 用递归法计算从 n个人中选择 k个人组
成一个委员会的不同组合数。
? 分析:
由 n个人里选 k个人的组合数
=由 n-1个人里选 k个人的组合数
+由 n-1个人里选 k-1个人的组合数
当 n==k或 k==0时,组合数为 1






使

#include<iostream.h>
void main()
{ int n,k;
int comm(int n,int k);
cin>>n>>k;
cout<<comm(n,k) <<endl;
}
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) ;
}
运行结果:
18 5
8568
前一页 休息 39
例 3-10汉诺塔问题
有三根针 A,B,C。 A针上有 N个盘子,
大的在下,小的在上,要求把这 N个盘子从 A
针移到 C针,在移动过程中可以借助 B针,每
次只允许移动一个盘,且在移动过程中在三
根针上都保持大盘在下,小盘在上。






使

A B C
分析:
将 n 个盘子从 A针移到 C针可以分解为下面三个步骤:
①将 A 上 n-1个盘子移到 B针上(借助 C针) ;
② 把 A针上剩下的一个盘子移到 C针上 ;
③ 将 n-1个盘子从 B针移到 C针上(借助 A针) ;
事实上,上面三个步骤包含两种操作:
①将多个盘子从一个针移到另一个针上,这是一个递
归的过程。 hanoi函数实现。
②将 1个盘子从一个针上移到另一针上。
用 move函数实现。
#include <iostream.h>
void move(char getone,char putone)
{ cout<< getone <<"-->"<<putone<<endl; }
void hanoi(int n,char one,char two,char three)
{ void move(char getone,char putone);
if (n==1) move (one,three);
else
{ hanoi (n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);
}
}
void main()
{
void hanoi(int n,char one,char two,char
three);
int m;
cout<<"Enter the number of diskes:";
cin>>m;
cout<<"the steps to moving "<<m<<"
diskes:"<<endl;
hanoi(m,'A','B','C');
}
运行结果:
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
前一页 休息 44
函数的参数传递机制
—— 传递参数值
? 在函数被调用时才分配形参的存储
单元。
? 实参可以是常量、变量或表达式。
? 实参类型必须与形参相符。
? 传递时是传递参数值,即单向传递。






使

前一页 休息 45
函数的参数传递机制
—— 参数值传递举例
X N
被调函数:
主调函数:
32.5A
D = power(A,3)
2.5 3
double power(double X,int N)






使

前一页 休息 46
例 3-11 输入两 整数交换后输出
#include<iostream.h>
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=5 y=10
前一页 休息 48
函数的参数传递 —— 用引用做形参
?引用 (&)是标识符的别名,例如,
int i,j;
int &ri=i;
//建立一个 int型的引用 ri,并将其
//初始化为变量 i的一个别名
j=10;
ri=j;//相当于 i=j;
?声明一个引用时,必须同时对它进行初始化,
使它指向一个已存在的对象。
?一旦一个引用被初始化后,就不能改为指向
其它对象。
?引用可以作为形参
void swap(int& a,int& b) {...}






使

前一页 休息 49
例 3-12 输入两个整数交换后输出
#include<iostream.h>
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
t=a;
x
5
t
5
x 的地址
a
x y
5 10
y 的地址x 的地址
a b
y 的地址x 的地址
a b
x
10
y
10
a=b b=t;
y
5
t
5
y 的地址
b
x y
10 5
Swap(x,y);
前一页 休息 52
例 3-13 引用调用举例
#include <iostream.h>#include <iomanip.h>
void fiddle(int in1,int &in2);int main()
{ int count = 7,index = 12;cout << "The values are ";
cout<<setw(5)<<count;cout<<setw(5)<<index<<endl;
fiddle(count,index);cout << "The values are ";
cout<<setw(5)<<count;cout<<setw(5)<<index<<endl;
return 0;}






使

void fiddle(int in1,int &in2)
{
in1 = in1 + 100;
in2 = in2 + 100;
cout << "The values are ";
cout<<setw(5)<<in1;
cout<<setw(5)<<in2<<endl;
}
运行结果:
The values are 7 12
The values are 107 112
The values are 7 112
前一页 休息 54
内联函数声明与使用
? 声明时使用关键字 inline。
? 编译时在调用处用函数体进行替换,节
省了参数传递、控制转移等开销。
? 注意:
– 内联函数体内不能有 循环语句 和 switch
语句。
– 内联函数的声明必须出现在内联函数第
一次被调用之前。
– 对内联函数不能进行异常接口声明。




前一页 休息 55
例 3-14 内联函数应用举例
#include<iostream.h>
inline double CalArea(double radius)
{
return 3.14*radius*radius;
}
int main()
{
double r(3.0);
double area;
area=CalArea(r);
cout<<area<<endl;
return 0;
}




前一页 休息 56
缺省形参值的作用
? 函数在声明时可以预先给出默认的形参值,
调用时如给出实参,则采用实参值,否则采
用预先给出的默认形参值。
? 例如:
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
}









前一页 休息 57
缺省形参值的说明次序
? 缺省形参值必须 从右向左 顺序声明,并
且在缺省形参值的右面不能有非缺省形
参值的参数。因为调用时实参取代形参
是从左向右的顺序。
? 例:
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); //错误









前一页 休息 58
缺省形参值与函数的调用位置
? 调用出现在函数体实现之前时,缺省形参值必
须在函数原形中给出;而当调用出现在函数体
实现之后时,缺省形参值需在函数实现时给出。
? 例:
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=5,int y=6)
{ return x+y; }
void main(void)
{ add(); //调用在实现后
}









前一页 休息 59
缺省形参值的作用域
? 在相同的作用域内,缺省形参值的说明应
保持唯一,但如果在不同的作用域内,允
许说明不同的缺省形参。
? 例:
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)
}









前一页 休息 60
例 3-15带缺省形参值的函数举例
#include <iostream.h>
#include <iomanip.h>
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 " ;
cout << get_volume(x,y,z) << endl;
cout << "Some box data is " ;
cout << get_volume(x,y) << endl;
cout << "Some box data is " ;
cout << get_volume(x) << endl;
cout << "Some box data is ";
cout << get_volume(x,7) << endl;
cout << "Some box data is ";
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
Some box data is 10 12 3 360
Some box data is 10 2 3 60
Some box data is 10 7 3 210
Some box data is 5 5 5 125
前一页 休息 62
重载函数的声明
? C++允许功能相近的函数在相同的作用
域内以相同函数名声明,从而形成重载。
方便使用,便于记忆。
? 例:
形参类型不同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);




前一页 休息 63
注意事项
– 不要将不同功能的函数声明为重载函数,以免出
现调用结果的误解、混淆。这样不好:
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; }




– 重载函数的形参必须不同, 个数不同或类型不同。
– 编译程序将根据实参和形参的类型及个数的最佳
匹配来选择调用哪一个函数。
前一页 休息 64
例 3-16重载函数应用举例
编写三个名为 add的重载函数,分别实现两整数
相加、两实数相加和两个复数相加的功能。
#include<iostream.h>
struct complex
{
double real;
double imaginary;
};
void main(void)
{
int m,n;
double x,y;
complex c1,c2,c3;
int add(int m,int n);
double add(double x,double y);
complex add(complex c1,complex c2);
cout<<"Enter two integer,";
cin>>m>>n;
cout<<"integer
"<<m<<'+'<<n<<"="<<add(m,n)<<endl;
cout<<"Enter two real number,";
cin>>x>>y;
cout<<"real number "<<x<<'+'<<y<<"= "<<add(x,y)
<<endl;
cout<<"Enter the first complex number,";
cin>>c1.real>>c1.imaginary;
cout<<"Enter the second complex number,";
cin>>c2.real>>c2.imaginary;
c3=add(c1,c2);
cout<<"complex number (" <<c1.real<< ','
<< c1.imaginary <<")+("<<c2.real<<','
<<c2.imaginary<<")=("<<c3.real<<','
<<c3.imaginary<<")\n";
}
int add(int m,int n)
{ return m+n; }
double add(double x,double y)
{ return x+y; }
complex add(complex c1,complex c2)
{
complex c;
c.real=c1.real+c2.real;
c.imaginary=c1.imaginary+c2.imaginary;
return c;
}
运行结果:
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)
前一页 休息 69
函数模板的声明
? 函数模板可以用来创建一个通用功能
的函数,以支持多种不同形参,进一
步简化重载函数的函数体设计。
? 声明方法:
template <typename 标识符 >
函数声明




前一页 休息 70
例 3-17 求绝对值函数的模板
#include<iostream.h>
template<typename T>
T abs(T x)
{ return x<0?-x:x; }
void main()
{ int n=-5;
double d=-5.5;
cout<<abs(n)<<endl;
cout<<abs(d)<<endl;
}




? 运行结果:
5
5.5
? 分析
– 编译器从调用 abs()时实参的类型,推导出
函数模板的类型参数。例如,对于调用表达
式 abs(n),由于实参 n为 int型,所以推导出
模板中类型参数 T为 int。
– 当类型参数的含义确定后,编译器将以函数
模板为样板,生成一个函数:
int abs(int x)
{ return x<0?-x:x; }
前一页 休息 72
C++系统函数
? C++的系统库中提供了几百个函数可
供程序员使用。
例如:求平方根函数( sprt)、求绝对
值函数( abs)等。
? 使用系统函数时要包含相应的头文件。
例如,math.h
使
用C++




前一页 休息 73
例 3-18系统函数应用举例
? 题目:
从键盘输入一个角度值,求出该角度的正
弦值、余弦值和正切值。
? 分析:
系统函数中提供了求正弦值、余弦值和正
切值的函数,sin(),cos(),tan(),函数的
说明在头文件 math.h中。
使
用C++




#include<iostream.h>
#include<math.h>
const double pi(3.14159265);
void 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;
}
运行结果:
30
sin(30)=0.5
cos(30)=0.866025
tan(30)=0.57735
前一页 休息 76
查找系统函数的使用说明
? 查编译系统的库函数手册
? 查联机帮助 —— VC++6.0联机帮助的使用方法:
help/Contents
->(“活动子集”栏 )Visual C++ Documentation
-> Visual C++ Documentation
->Using Visual C++
-> Visual C++ Programmer's Guide
-> Run-Time Library Reference
->Run Time Routines by Category
-> Run Time Routines by Category
使
用C++




前一页 休息 77
作 业
? 复习第三章,预习第四章
? 3-2,3-8,3-11,3-13,3-15
? 学习使用联机帮助系统查找系统函数
? 实验三