1
C++程序设计语言——V3.0
计算机学院: 张 永 鸣
Tel:82317623/7613
E-mail:zym@buaa.edu.cn
2
第三章 函数
z 函数的定义与使用
z 函数间的参数传递
z 内联函数
z 带默认形参值的函数
z 函数重载
z 使用C++系统函数
z 函数
(第九章介绍)
3
模块化程序设计
函数的定义与使用函数的定义与使用)模块化程序设计无论是C还是C++都是极为重要的概念。
)函数是面向对象程序设计中的基本抽象单元,是对功能的
抽象。
A
B
D
C
E F G
IH
主模块
子模块
层次分明
功能抽象
逻辑清晰 自顶向下
简化、可靠、快速、高效、可维护性简化、可靠、快速、高效、可维护性
4
程序结构函数的定义与使用
)程序结构)程序结构
void main()
{
...
fb( );
...
fc( );
...
}
fb( )
{
...
fd( );
...
fe( );
...
}
fc( )
{
...
fe( );
...
ff( );
…
fg( );
}
fd( )
{
...
fh( );
...
}
fe( )
{
...
fh( );
...
}
fh( )
{
...
...
}
ff( )
{ }
fg( )
{
fi( );
}
fi( )
{
...
...
}
函数可以
递归调用
函数可以
嵌套调用
恢复:
主调程序现场
返回地址
保存:
返回地址
当前现场
5
函数定义、函数声明和函数调用
函数的定义与使用函数的定义与使用void main()
{ double new_style( int a, double x ) ; //函数声明
double x, y;
int a;
……
y = new_style( a, x ); //函数调用
……
}
double new_style( int a, double x ) // 函数定义
{
// 函数体
}
6
定义函数
A. 在函数体中用 return 语句实现函数的返值。
B. 不允许再出现定义函数。(即函数不能嵌套定义)。
C. 缺省,为空函数。(调试程序时先调用,后补功能)
[ auto ]
register
static
extern
a.函数返回值的数据类型。
b.缺省时,隐含 int 型返值。
c.函数无返值时,可用void
表示 。
同标识符
方法命名
A. 被初始化的内部变量。
B. 寿命和可见性仅限于函数内部。
C. 若无参数,可写void。
函数的定义与使用函数的定义与使用
[存储类型] [数据类型] 函数名([形参表])
{
[内部数据说明 ]
[函数体]
}
7
函数的返回函数的定义与使用函数的定义与使用
return(表达式) ;
return 表达式 ;
return ;
return [ [( ] [表达式] [ )] ];
① return 语句只能把一个返值传递给调用函数。
② 若 return 语句中表达式类型与函数返回值的数据
类型不一致,则转换为函数类型。
③ 返回值可以是数值,也可以是地址。当返值是地
址时,应该用指针接受它(即函数定义为指针型函
数。
8
函数声明
函数的定义与使用函数的定义与使用
① 含类型说明的形参表
[名1], [名2 ], ..., [名n ]
② TC 2.0v 可省、C++时,不能省
调用函数:
函数名( [实参表] );
(允许嵌套调用、递归调用)
)当被调函数不可见时,调用该函数之前必须声明函数原型。
[存储类型] [数据类型] 函数名([形参表]) ;
9
例3.1 求组合数
#include
void main() // 3_1.cpp c5_2.cpp
{ int m, n, cmn( int, int );
scanf("%d,%d", &m, &n); // 输入:7,5↵ != 7 5 ↵
printf("Cmn=%d\n", cmn(m, n) );
}
cmn(int x, int y )
{ int facto( int );
if( x>=y && y>=0 )
return( facto( x) / facto( y ) / facto( x-y ) ) ;
else
return( 0 );
}
函数的定义与使用函数的定义与使用
公式: =
要求: m≥ n≥ 0 且 =1
Cnm )!(!
!
nmn
m
−
0
mC
嵌套调用
10
例3.1 求组合数(续)
// function for n!
facto (int k ) //3_1.cpp
{ int i, m=1;
if( k==0 )
return( 1 ) ;
else
{
for( i=2; i<=k; i++ )
m *= i;
return( m );
}
}
函数的定义与使用函数的定义与使用
//3_1.cpp 4,2 ↵ = = 6
7,5 ↵ = = 21
7,6 ↵ = = 7
8,7 ↵ = = -5
C24 )!24(!2
!4
−
C57 )!57(!5
!7
−
C67 )!67(!6
!7
−
C78 )!78(!7
!8
−
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11
例3.2 数制转换函数的定义与使用函数的定义与使用
例3.2 输入一个8位二进制数,将其转换为
十进制数输出。
分析:11012 = 1(23) + 1(22) + 0(21) + 1(20)
= 1310
所以,如果输入00001101,则应输出13
12
例3.2 数制转换(续)
函数的定义与使用函数的定义与使用#include //3_2.cppdouble power(double x, int n);
void main(void)
{ int i, 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 "<
void main( void )
{ int c;
if((c=getchar( ) )!=EOF )
{ putchar( c );
main( );
}
}
方法二:递归实现。
//3_3_2.cpp recursion
#include
void main( void )
{ int c;
if((c=getchar( ) )!=EOF )
{ putchar( c );
main( );
}
}
函数的定义与使用方法一:非递归实现。
//3_3_1.cpp non recursion
#include
void main( void )
{ int c;
while((c=getchar( ) )!=EOF)
putchar( c );
}
方法一:非递归实现。
//3_3_1.cpp non recursion
#include
void main( void )
{ int c;
while((c=getchar( ) )!=EOF)
putchar( c );
}
//3_3_2.cpp//3_3_1.cpp
递归调用
15
例例3.4 3.4 递归函数程序。
例3.4 把输入的一篇文本文件以正向和逆向输出到屏幕上。
#include //3_4.cpp recursion
void main( void )
{ int c;
if( ( c = getchar() ) != EOF )
{ putchar( c );
main( );
putchar( c );
}
else
{ putchar( '\n' );
putchar( '\n' );
}
}
函数的定义与使用函数的定义与使用
//3_4.cpp
16
例3.5 求n!
分析:计算n!的公式如下:
是递归形式的公式,前面求组合数中n!
用非递归形式实现,现用递归函数求n!。
函数的定义与使用函数的定义与使用
⎩⎨
⎧
>−
==
)0()!1(
)0(1
!
nnn
n
n
17
例例3.5 3.5 求n!程序。
long facto(long n)
{
if (n<0)
{cout<<"n<0,data error!"<
void main( ) //3_5.cpp
{
long facto(long n);
int n;
cout<<"Enter a positive integer:";
cin >> n;
cout<
void main( ) //3_5.cpp
{
long facto(long n);
int n;
cout<<"Enter a positive integer:";
cin >> n;
cout<题
— — 古 代 印 度 布 拉 玛 庙 里 僧 侣 们 玩 的 游 戏 。
N o . 1
N o . 6 1
N o . 6 2
N o . 6 3
N o . 64
N o . 1
N o . 61
N o . 6 2
N o . 6 3
N o . 6 4
铜 板
金
盘
100万 年 (计 算 一 次 移 动 /微 秒 ) 移 动 条 件 : 一 次 移 一 个 。
5800亿 年 (计 算 一 次 移 动 /秒 ) 只 能 小 压 大 。
615,511,709,073,744,466,181264 =−
a b c
A->C
19
例例3.6 汉诺塔(2/3)
函数的定义与使用函数的定义与使用分析:
将 n 个盘子从A柱移到C柱可以分解为下面三个步骤:
①将 A 上n-1个盘子移到 B 柱上(借助C柱);
②把 A 柱上剩下的一个盘子移到 C 柱上;
③将n-1个盘子从B柱移到C柱上(借助A柱);
事实上,上面三个步骤包含两种操作:
①将多个盘子从一个柱移到另一个柱上,这是一个递归的过
程。 hanoi函数实现。
②将1个盘子从一个柱上移到另一柱上。
用move函数实现。
20
例例3.6 汉诺塔(3/3)
#include
//3_6_1.cpp
void hanoi( int n, char a, char b, char c )
{ if( n>0 )
{ hanoi(n-1, a, c, b) ;
cout <<"Move disc "<> n;
hanoi( n, 'a', 'b', 'c' );
}
函数的定义与使用函数的定义与使用 //3_6_1.cpp
21
函数的参数传递
函数的定义与使用函数的定义与使用…………函数名函数名( )( )
{{
funcfunc((实参表实参表););
}}
…………funcfunc((形参表形参表))
{{
…………
}}
1. 实参与形参的个数、顺序、数据类型必须一致。
2. 在函数被调用时才分配形参的存储单元。
3. 实参可以是常量、变量或表达式(是形参的初值)。
4. 传递时是传递参数值,即复制传递。
5. 使用地址作为参数,即地址传递(可返处理结果)。
6. 数组作为参数时的情况:
– 数组元素作实参,与单个变量一样。
– 数组名作参数,传送的是数组首地址(可回带结果)。
7. 形参以引用接收实参,即地址传递(可返处理结果)。
* 使用全局变量传递数据
22
例:复制与地址方式传递数据
函数的定义与使用函数的定义与使用
例3.8 引用地址接收
#include //3_8.cpp
void main()
{ int a=2, b=5;
void swp1( int &, int & );
cout << a << " " << b << endl;
swp1( a, b );
cout << a << ", "<< b << endl;
}
void swp1( int &x, int &y )
{ int r;
r = x;
x = y;
y = r;
cout << x << "; "<< y << endl;
}
例3.8 引用地址接收
#include //3_8.cpp
void main()
{ int a=2, b=5;
void swp1( int &, int & );
cout << a << " " << b << endl;
swp1( a, b );
cout << a << ", "<< b << endl;
}
void swp1( int &x, int &y )
{ int r;
r = x;
x = y;
y = r;
cout << x << "; "<< y << endl;
}
例3.7 复制数据传递
#include //3_7.cpp
void main()
{ int a=2, b=5;
void swp1( int , int );
cout << a << " " << b << endl;
swp1( a, b );
cout << a << ", " << b << endl;
}
void swp1( int x, int y )
{ int r;
r = x;
x = y;
y = r;
cout << x << "; "<< y << endl;
}
例3.7 复制数据传递
#include //3_7.cpp
void main()
{ int a=2, b=5;
void swp1( int , int );
cout << a << " " << b << endl;
swp1( a, b );
cout << a << ", " << b << endl;
}
void swp1( int x, int y )
{ int r;
r = x;
x = y;
y = r;
cout << x << "; "<< y << endl;
}
//3_8.cpp//3_7.cpp
23
例3.9 数组名和数组元素传递数据。
#include
void main(void)
{ int a[2], b[2], c;
int func1( int x[], int s, int t );
a[0]=a[1]=b[0]=b[1]=1;
cout<<"-1-"< //3_10.cpp
inline double CalArea(double radius)
{
return 3.14*radius*radius;
}
void main( )
{
double r(3.0); //for VC 6.0
//double r=3.0; //for VC 6.0 and TC 3.0
double area;
area=CalArea(r);
cout << area << endl;
}
//3_10.cpp
28
缺省形参值的作用
带缺省参数的函数带缺省参数的函数void void funcfunc((intint a,a, intint b= b= --1);1);
调用调用funcfunc函数的方法:函数的方法:funcfunc(1, 2); (1, 2); 或或 funcfunc(1); (1);
例例3.113.11
int add(int x=5, int y=6) { return x+y; }int add(int x=5, int y=6) { return x+y; }
#include //3_11.cpp
void main(void)
{ cout << add(10, 20) << endl; //30=10+20
cout << add(10) << endl; //16=10+6
cout << add( ) << endl; //11=5+6
}
#include //3_11.cpp
void main(void)
{ cout << add(10, 20) << endl; //30=10+20
cout << add(10) << endl; //16=10+6
cout << add( ) << endl; //11=5+6
}
//3_11.cpp
29
参数缺省值的位置和应用参数缺省值的位置和应用
带缺省参数的函数带缺省参数的函数)) 一般在函数原型中指定参数缺省值,且只有参数列表一般在函数原型中指定参数缺省值,且只有参数列表
的后部参数才可以是缺省的。的后部参数才可以是缺省的。
void f(void f(intint a,a, intint b= b= --1,1, intint c=10); //c=10); //正确正确
void f(void f(intint a,a, intint b= b= --1,1, intint c); //c); //错误错误
void f(void f(intint a,a, intint c,c, intint b= b= --1); //1); //正确正确
)) 一旦使用某个缺省参数值,则这个参数后面的所有缺一旦使用某个缺省参数值,则这个参数后面的所有缺
省值都被使用。省值都被使用。
f(2, 4, 6); f(2, 4, 6); 或或 f(2); f(2); 或或 f(2, 4); //f(2, 4); //正确正确
f(2, ,4); //f(2, ,4); //错误错误
30
参数缺省值和添加及占位符参数缺省值和添加及占位符
带缺省参数的函数带缺省参数的函数)) 一般参数缺省值也可以是全局变量、全局常量,甚至
是一个函数调用。
int d= -10;
void f2(int a, int b= -1, int c=d); //正确
int setclock( time_t start_time = time(NULL) );
)) 在函数声明中,一旦定义了缺省值,就不能再定义它,
但可以添加一个或多个缺省值。
void f3(int a, int b, int c=0);
void f3(int a, int b=1, int c); //正确添加b的缺省值
void f3(int a, int b=1, int c=0); //错误,重定义b、c
)) 缺省参数可以让声明的参数没有标识符。
void func(int x, int =0, float f=1.1);
void func(int x, int, float f) { /*…可用x,f…*/}
用func函数:func(1); 或func(1, 2, 3.0);
占位符(placeholder)
//3_12.cpp
31
参数个数不定应用参数个数不定应用
带缺省参数的函数带缺省参数的函数)) 有些函数有时不能明确参数类型和参数个数,此时可有些函数有时不能明确参数类型和参数个数,此时可
利用利用......符,它表示符,它表示参数个数不定参数个数不定。。
(在(在stdargstdarg.h.h头文件提供了一系列处理头文件提供了一系列处理……参数例程)参数例程)
例3.13 输出错误信息
void error( int severity …) { …... }
…...
error(0, "system", "error");
error(1, "can't", "open" , "printer" );
error(2, "error");
…...
//3_13.cpp
32
重载函数
))一词多义,一词多义,从上下文中理解其含义。从上下文中理解其含义。
playplay the piano(basketballthe piano(basketball、、footballfootball、、…………))
)) C++C++允许功能相近的函数在同作用域内定义同名函数,允许功能相近的函数在同作用域内定义同名函数,
从而形成重载。方便使用,便于记忆。从而形成重载。方便使用,便于记忆。
int abs(int i);
long labs(long l);
double dabs(double d);
…… // in C
abs(-10);
labs(-100000);
dabs(-12.34);
int abs(int i);
long labs(long l);
double dabs(double d);
…… // in C
abs(-10);
labs(-100000);
dabs(-12.34);
int abs(int i);
long abs(long l);
double abs(double d);
… // in C++(内用名字变异)
abs(-10);
abs(-100000);
abs(-12.34);
int abs(int i);
long abs(long l);
double abs(double d);
… // in C++(内用名字变异)
abs(-10);
abs(-100000);
abs(-12.34);
重载函数的要求:同名函数至少在参数个数
或参数类型上有所不同。
33
使用重载函数时一些限制
))编译程序将根据实参和形参的类型及个数的最佳匹配来
选择调用哪一个函数。
)选择使用哪一个重载函数的过程,与C++处理其它的模
糊问题一样。
abs('A'); abs(12.45F);
找不到相应的函数声明,就调用最便于进行
类型转换的那个函数。
函函
数数
重重
载载
int add(int x, int y);
int add(int a, int b);
编译器不以形参名来区分
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);
void add(int x, int y);
编译器不以返回值来区分
typedef INT int;
void func(int i);
void func(INT i); 参数的类型实际相同
typedef INT int;
void func(int i);
void func(INT i); 参数的类型实际相同
34
形参中的限定符const
))const 以及函数中引用的指针可以通过使用const
限定符来区分。
void func(char *ch);
void func(const char *ch);
……
void main( )
{ const char c1 = 'a';
char c2 = 'b';
func(&c1); //calls void func(const char *ch);
func(&c2); //calls void func(char *ch);
}
//3_14.cpp
函函
数数
重重
载载
35
坏的编程风格
))让重载函数执行不同的功能是非常坏的编程风格,
同名函数应该具有相同的功能。
int add(int x,int y)
{ return x+y; }
int add(int x,int y)
{ return x+y; }
float add(float x,float y)
{ return x-y; }
float add(float x,float y)
{ return x-y; }
)不要同时使用重载函数和缺省参数的函数,因为
当调用函数时少写一个参数,系统无法判定是利用
重载函数还是利用缺省参数的函数,会发生错误。
函函
数数
重重
载载
36
例3.15 重载函数应用举例
编写三个名为add的重载函数,分别实现两整数相
加、两实数相加和两个复数相加的功能。
#include //3_15.cpp
struct complex
{
double real;
double imaginary;
};
//3_15.cpp
函函
数数
重重
载载
37
例3.15 重载函数应用举例
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 "<>x>>y;
cout<<"real number "<>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";
}
//3_15.cpp
函函
数数
重重
载载
38
例3.15 重载函数应用举例
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)
//3_15.cpp
重载的重要应用是
构造函数
重载的重要应用是重载的重要应用是
构造函数构造函数
函函
数数
重重
载载
39
函数模板的声明
函函
数数
模模
板板
))函数模板可以用来创建一个通用功能的函
数,以支持多种不同形参,进一步简化重
载函数的函数体设计。
))声明方法:
template
函数定义
40
例3.16 求绝对值函数的模板
#include
template
T abs(T x)
{ return x<0 ? –x : x;}
void main( )//3_16.cpp
{ int n= -5;
double d= -5.5;
cout<
42
例3.17 系统函数应用举例使用使用
C
++
C
++ 系统函数系统函数
)题目:
从键盘输入一个角度值,求出该角度的正弦值、
余弦值和正切值。
)分析:
系统函数中提供了求正弦值、余弦值和正切值
的函数:sin( )、cos( )、tan( ),函数的说明
在头文件math.h中。
43
例3.17 举例程序
#include
#include
const double pi(3.14159265);
void main(void) //3_17.cpp
{ double a,b;
cout << "Enter a number ";
cin >> a;
b=a*pi/180;
cout<<"sin("<目录 ”标签)点开“MSDN Library Visual Studio 6.0”
¼ 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
45
小结
函数函数)函数的定义与使用
)函数间的参数传递
)内联函数
)带默认形参值的函数
)函数重载
)使用C++系统函数
)函数模板(第九章介绍)
)函数的定义与使用
)函数间的参数传递
)内联函数
)带默认形参值的函数
)函数重载
)使用C++系统函数
)函数模板(第九章介绍)
46
本章作业
函数函数
)复习第三章,预习第四章
)思考题:3-2~3-6, 3-11
)上机题:3-8, 3-13~3-15
)实验三
)学习使用联机帮助系统查找系统函数
)复习第三章,预习第四章
)思考题:3-2~3-6, 3-11
)上机题:3-8, 3-13~3-15
)实验三
)学习使用联机帮助系统查找系统函数
C++程序设计语言——V3.0
第三章 函数
模块化程序设计
程序结构
函数定义、函数声明和函数调用
定义函数
函数的返回
函数声明
例3.1 求组合数
例3.1 求组合数(续)
例3.2 数制转换
例3.2 数制转换(续)
递归调用
例3.3 把输入的内容忠实的输出到屏幕上
例3.4 递归函数程序。
例3.5 求n!
例3.5 求n!程序。
例3.6 汉诺塔(1/3)
例3.6 汉诺塔(2/3)
例3.6 汉诺塔(3/3)
函数的参数传递
例:复制与地址方式传递数据
例3.9 数组名和数组元素传递数据。
const在函数参数和返值中的应用
const在函数参数和返值中的应用(续)
内置函数声明与使用
例3.10 内置函数应用举例
缺省形参值的作用
参数缺省值的位置和应用
参数缺省值和添加及占位符
参数个数不定应用
重载函数
使用重载函数时一些限制
形参中的限定符const
坏的编程风格
例3.15 重载函数应用举例
例3.15 重载函数应用举例
例3.15 重载函数应用举例
函数模板的声明
例3.16 求绝对值函数的模板
C++系统函数
例3.17 系统函数应用举例
例3.17 举例程序
查找系统函数的使用说明
小结
本章作业