第 4章 函数
4.1 函数的定义和使用
4.2 函数的参数传递
4.3 函数的嵌套调用
4.4 函数的递归调用
4.5 内联函数
4.6 函数重载
4.7 带默认形参值的函数
4.8 函数模板
4.1 函数的定义与使用系统库函数:需要包含相应的头文件,如 #include <math.h>
系统所提供的库函数都是完成一些通用功能的函数,在实际的程序设计中,自己还需要编写大量完成特殊功能的函数,我们称之为用户自定义函数。
4.1.1 函数的定义函数定义的一般格式为:
函数类型 函数名( 形式参数表)
{
语句组
}
第 4章 函数函数头函数体例 4.1 求两个整数中较大的值的函数
#include <iostream.h>
int max(int x,int y);
void main()
{
int a,b,c;
cout << "请输入两个整数,用空格分隔,";
cin >> a >> b;
c = max(a,b);
cout << "a,b中较大的数是," << c << endl;
}
int max(int x,int y)
{
int m;
if(x>y) m=x;
else m=y;
return m;
}
程序运行结果为:
请输入两个整数,用空格分隔,20 30
a,b中较大的数是,30
第 4章 函数函数调用语句主调函数被调函数
4.1 函数的定义与使用
4.1.1 函数的定义(续一)
1,函数类型与返回值函数的类型 就是函数返回值的类型,可以是 C++中的任何数据类型。
函数的返回值 由函数体中的 return语句给出。
return语句的一般格式为:
return (表达式);
或 return 表达式;
或 return ;
函数的类型要与 return语句的返回值 类型相同 。若不一致则以函数类型为准,将返回值类型转换为函数类型,若不能转换,则编译过程会出错!
若函数不需要返回值,则应将类型指定为 void。
第 4章 函数执行该语句时,不带回返回值,只是返回主调函数
4.1 函数的定义与使用
4.1.1 函数的定义(续二)
2,函数的参数函数通过参数接收数据。
函数头中的参数为 形式参数 (或形参),在函数调用时给出的参数称为 实际参数 (或实参)。
实参必须是一个实际的值,而形参只有在发生实际调用时才有具体的值。
如果不需要为函数提供数据,则可以没有参数,称之为无参函数。
第 4章 函数
4.1 函数的定义与使用
4.1.2 函数的声明与调用函数调用的一般格式:
函数名(实际参数表)
注意,若无形参,也不必给出实参,但括号不能省略。
实参与形参个数应一致,类型要一一对应。
函数原型声明,在调用函数前,对函数进行说明。通常用于函数定义出现在函数调用之后的情况。
函数原型声明的一般格式:
函数类型 函数名(形式参数表);
注意,函数原型中可以不写出参数名。
如,double power(double,int);
第 4章 函数例 4.2 编写一个求 x的 n次方的函数
#include <iostream.h>
double power(double x,int n);
void main()
{
double x;
int n;
cout << "请输入 X和 N的值,用空格分隔,";
cin >> x >> n;
cout << x << "的 " << n << "次方是," << power(x,n) <<
endl;
}
double power(double x,int n)
{
double a = 1.0;
int i;
for(i=1; i<=n; i++)
a *= x;
return a;
}
程序运行结果为:
请输入 X和 N的值,用空格分隔,3.0 4
3的 4次方是,81
第 4章 函数返 回
4.2 函数的参数传递根据函数的功能,函数的形参可以选择为数值、引用或指针三种形式。
4.2.1 值传递
C++中参数的传递是 单向 的,即 只能将实参的值传递给形参,
而 形参值的改变对实参没有影响 。
例 4.3 写一个函数交换主函数中两个变量的值考虑:使用值传递,将需要交换的变量作为实参传递给函数的形参,在函数体中完成交换值的过程,这种做法能否实现程序要求?
第 4章 函数例 4.3 源程序
#include <iostream.h>
void swap(int x,int y);
void main()
{
int a,b;
a = 10;
b = 20;
swap(a,b);
cout << a << "," << b << endl;
}
程序运行结果为:
10,20
10a
10x
20 b
20 y 10 20x y
10temp
20 20x y
10temp
20 10x y
10temp
void swap(int x,int y)
{
int temp;
temp = x;
x = y;
y = temp;
}
第 4章 函数注意:值传递时,函数的实参与形参在内存中占用不同的存储空间,值只能由实参传递给形参,而形参的变化并不会影响实参。因此不能完成程序功能。
4.2 函数的参数传递
4.2.2 使用引用
1,引用的概念引用是一种特殊的变量,可以认为是一个变量的别名。
定义引用的一般格式,
类型说明符 &引用名 = 变量名;
如,int a = 1;
int &b = a;
首先定义变量 a,并初始化为 1,然后定义引用 b,并初始化为 a,
即 b是 a的别名,因此 a和 b是同一个单元 。
注意,定义引用时一定要初始化,指明该引用变量是谁的别名 。
第 4章 函数例 4.4 引用的使用
#include <iostream.h>
void main()
{
int a=1;
int c=10;
int &b = a;
cout << a << "," << b <<"," << c << endl;
b = c;
cout << a << "," << b <<"," << c << endl;
b = 20;
cout << a << "," << b <<"," << c << endl;
}
程序运行结果:
1,1,10
10,10,10
20,20,10
定义引用 b,并将其作为 a的别名第 4章 函数将 c的值赋给 b,不是将 b作为 c的别名
4.2 函数的参数传递
4.2.2 使用引用(续一)
2,引用作为函数的参数例 4.5 写一个函数,用引用作为参数,交换函数中两个变量的值。
第 4章 函数
#include <iostream.h>
void swap(int x,int y);
void main()
{
int a,b;
a = 10;
b = 20;
swap(a,b);
cout << a << "," << b << endl;
}
void swap(int &x,int &y)
{
int temp;
temp = x;
x = y;
y = temp;
}
程序运行结果为:
20,10
4.2 函数的参数传递
4.2.2 使用引用(续二)
例 4.5程序运行过程中参数值的变化图示第 4章 函数
10a
x
b
y
20
a
x
b
y
10temp
20
a
x
b
y
10temp
10
a
x
b
y
10temp
20 20 20 10
注意:引用作参数时,函数的实参与形参在内存中共用存储单元,因此形参的变化会使实参同时变化。
返 回
4.3 函数的嵌套调用函数的 嵌套调用 是指,在执行被调用函数时,被调用函数又调用了其它函数。
嵌套调用的执行过程,
第 4章 函数例 4.6 求 1! + 2! + … n!
#include <iostream.h>
long f1(int n);
long f2(int m);
void main()
{
int n;
long s;
cout << "请输入 n的值,";
cin >> n;
s = f1(n);
cout << s << endl;
}
第 4章 函数
long f1(int n) // 求 1!+2!+… +n!
{
int i;
long sum = 0;
for(i=1; i<=n; i++)
sum += f2(i);
return sum;
}
long f2(int m) // 求 m!
{
int i;
long s = 1;
for(i=1; i<=m; i++)
s *= i;
return s;
}
程序运行结果:
请输入 n的值,5
153
例 4.7 输入两个整数,求平方和
#include <iostream.h>
int fun2(int m);
int fun1(int x,int y);
void main(void)
{
int a,b;
cout << "请输入两个整数,";
cin >> a >> b;
cout << "a,b的平方和," << fun1(a,b) << endl;
}
int fun1(int x,int y)
{
return ( fun2(x) + fun2(y) );
}
int fun2(int m)
{
return ( m*m );
}
第 4章 函数程序运行结果:
请输入两个整数,4 5
a,b的平方和,41
返 回
4.4 函数的递归调用一个函数在它的函数体内,直接或间接地调用它自身,称为递归调用 。这种函数称为 递归函数 。
直接或间接调用函数自身的情况如图,
第 4章 函数
f函数调用 f函数
f1函数调用 f2函数
f2函数调用 f1函数直接调用 间接调用这两种递归调用都是无中止地调用自身,显然是不正确的。为了防止递归调用无终止地进行,必须在函数内有 终止递归调用的手段 。
例 4.8 用递归的方法求 n!
分析:计算 n!的公式为,
1 n =1或 0
y =
n*(n-1)! n > 0
可以将求 n!转化为求 (n-1)!,再继续转化为求 (n-2)!,到 1时应停止递归
#include <iostream.h>
long power(int n);
void main()
{
int n;
long y;
cout << "input a integer number:";
cin >> n;
y=power(n);
cout << n << "!=" << y << endl;
}
第 4章 函数
long power(int n)
{
long f;
if(n>1)
f=n*power(n-1);
else
f=1;
return f;
}
例 4.8的递归过程分析第 4章 函数例 4.9 Hanoi塔问题有三根针 A,B,C,A针上有 n个盘子,盘子大小不等,大的在下,小的在上,如图所示。要求将这 n个盘子从 A针移到 C针,
在移动过程中可以借助 B针,每次只能移动一个盘子,并且在移动过程中三根针上的盘子都保持大盘在下,小盘在上。
分析:将 n个盘子从 A针移到 C针可以分解为以下三个步骤:
( 1)将 A针上的 n-1个盘子借助 C针移到 B针上;
( 2)将 A针上剩下的一个盘子移到 C针上;
( 3)将 B针上的 n-1个盘子借助 A针移到 C针上。
第 4章 函数
A B C
n-1个盘子的
Hanoi问题例 4.9 Hanoi塔问题源程序
#include <iostream.h>
void Move(char x,char y);
void Hanoi(int n,char one,char two,char three);
void main()
{
int n;
cout << "请输入盘子数,";
cin >> n;
cout << n << "个盘子的移动过程为," << endl;
Hanoi(n,'A','B','C');
}
//函数 Move()将一个盘子从 x针移到 y针
void Move(char x,char y)
{
cout << x << "-->" << y << endl;
}
第 4章 函数例 4.9 Hanoi塔问题源程序(续)
//函数 Hanoi()将 n-1个盘子从 one针借助 two针移到 three针
void Hanoi(int n,char one,char two,char three)
{
if(n==1)
Move(one,three);
else
{
Hanoi(n-1,one,three,two);
Move(one,three);
Hanoi(n-1,two,one,three);
}
}
第 4章 函数程序运行结果:
A?C
A?B
C?B
A?C
B?A
B?C
A?C
返 回运行演示
4.5 内联函数内联函数 与一般函数的区别在于它不是在调用时发生控制转移,而是在编译时将被调函数体嵌入到每一个函数调用处,节省了参数传递、控制转移等开销。对于一些规模较小、频繁调用的函数可声明为内联函数,能提高程序运行效率。
内联函数的定义,
inline 类型说明符 函数名(参数及类型表)
{ 函数体 }
注意,只有简单的函数才能成为内联函数,如函数体中不能有循环语句和 switch语句等。
内联函数的定义必须出现在内联函数第一次被调用之前。
第 4章 函数例 4.10 使用内联函数
#include <iostream.h>
inline int Add(int a,int b)
{
int x;
x = a+b;
return x;
}
第 4章 函数程序运行结果:
10+20=30
10+50=60
50+50=100
返 回
void main()
{
int a,b,c;
a = 10;
b = 20;
c = Add(a,b);
cout << a << " + " << b << " = " << c << endl;
c = Add(a,50);
cout << a << " + 50 " << " = " << c << endl;
c = Add(50,50);
cout << "50 + 50 " << " = " << c << endl;
}
4.6 函数重载在 C++中可以定义多个相同名字的函数,只要它们 形参的个数或类型不完全一致 即可,编译程序根据实参与形参的类型及个数自动确定调用哪一个同名函数,这就是 函数重载,这些同名函数称为 重载函数 。
例 4.12 定义两个函数,分别求两个整数及两个实数的最大值。
分析:在 C语言中,要定义题中的函数,只能使用两个不同名函数实现,即定义 int max1(int x,int y)
double max2(double x,double y)
在 C++中,可通过定义两个重载函数实现,二者同名,如下:
int max(int x,int y)
double max(double x,double y)
第 4章 函数例 4.12 源程序
#include <iostream.h>
int max(int x,int y);
double max(double x,double y);
void main()
{
int a=10,b=20,c;
double x=200.3,y=400.6,z;
c = max(a,b);
z = max(x,y);
cout << c << " " << z << endl;
}
第 4章 函数程序运行结果:
int function
float function
20,400.6
返 回
int max(int x,int y)
{
cout << "int function" << endl;
if(x>y) return x;
else return y;
}
double max(double x,double y)
{
cout << "float function" << endl;
if(x>y) return x;
else return y;
}
4.7 带默认参数值的函数
4.7.1 带默认参数值的函数在函数的声明或定义中可以预先给出默认的形参值,函数调用时,按 从左到右 的次序将实参和形参结合,如给出对应的实参,
则采用实参值,否则采用预先给出的默认形参值 。
例 4.13 使用带默认参数值的函数求 x的 n次方( n是正整数)。
第 4章 函数
#include <iostream.h>
double power(double x=10.0,int n=2);
void main()
{
cout << power(3,5) << endl;
cout << power(3) << endl;
cout << power() << endl;
}
double power(double x,int n)
{
int i;
double s=1.0;
for(i=1; i<=n; i++)
s *= x;
return s;
}
4.7 带默认参数值的函数
4.7.1 带默认参数值的函数(续)
注意,默认形参值必须 由右向左 的顺序定义。如果某个参数有默认值,则其右面的参数必须都有默认值;如果某个参数没有默认值,则其左面的参数都不能有默认值。例如:
int max(int a,int b=10,int c=20); //正确
int max(int a,int b=10,int c); //错误
int max(int a=5,int b,int c=30); //错误在后两种情况下,调用语句 x = max(20,30); 会出错!
注意,在使用带默认参数值的函数时,只能在函数定义或函数声明中的一个位置给出默认值,不能在两个位置同时给出。还要保证在 函数调用之前给出默认值 。
第 4章 函数
4.7 带默认参数值的函数
4.7.2 带默认参数值函数产生的二义性例 4.14程序第 4章 函数
#include <iostream.h>
int add(int x=5,int y=6);
float add(int x=5,float y=10.0);
void main()
{
int a;
float b;
a= add(10,20);
b= add(10);
cout << "a= " << a << endl;
cout << "b= " << b << endl;
}
int add(int x,int y)
{
return x+y;
}
float add(int x,float y)
{
return x+y;
}
b=add(10)语句产生二义性,可以认为该语句是调用第一个函数,也可以是第二个,因此编译器不能确定调用的是哪一个函数。 返 回
4.8 变量的存储类别
4.8.1 内部变量与外部变量
1,内部变量在一个函数内部定义的变量是内部变量(也称为局部变量),
它只在该函数范围内有效。
例如,
第 4章 函数
void f1(int a)
{
int b,c;
……
}
void main()
{
int m,n;
……
}
a,b,c的有效范围
m,n的有效范围
int i,a;
……
for(i=0; i<10; i++)
{
int b;
……
}
b的有效范围注意:在不同的作用范围内允许声明同名的变量
4.8 变量的存储类别
4.8.1 内部变量与外部变量(续)
2,外部变量在函数外部定义的变量是外部变量(也称为全局变量),它不属于任何一个函数。它的作用范围是:从外部变量的定义位置开始,到本文件的结尾。
例如,
第 4章 函数
int a,b;
void f1( )
{
……
}
int x,y;
void main()
{
……
}
x,y的有效范围
a,b的有效范围例 4.15 使用全局变量和局部变量
#include <iostream.h>
int i=1; //全局变量,文件作用域
void main()
{
cout<<"全局变量 i="<<i<<endl; //输出 1
int i=5; //函数局部变量,块作用域
{
int i; //块局部变量,块作用域
i=7;
cout<<"块局部变量 i="<<i<<endl; //输出 7
cout<<"全局变量 i="<<::i<<endl; //输出 1,::使用全局变量
}
cout<<"函数局部变量 i="<<i<<endl; //输出 5
cout<<"全局变量 i="<<::i<<endl; //输出 1,::使用全局变量
}
第 4章 函数程序运行结果:
全局变量 i=1
块局部变量 i=7
全局变量 i=1
函数局部变量 i=5
全局变量 i=1
4.8 变量的存储类别
4.8.2 变量的存储类别变量在内存中的存储方式可以分为两大类,即 静态存储方式与 动态存储方式 。
静态存储方式 是指在程序运行期间,分配固定的存储空间。
全局变量 和 静态局部变量 是静态存储方式。
动态存储方式 是在程序执行过程中,根据需要动态地分配存储空间。 局部变量 是动态存储方式。
1,静态变量定义形式,static 数据类型 变量名特点,程序运行过程中变量始终存在,每次调用函数结束的值都被保留下来。
仅初始化一次,每次调用它所在的函数时,不再重新初始化。
若不指定初值,自动指定初值为 0。
第 4章 函数例 4.16 输出 1~4的阶乘
#include <iostream.h>
int fact( int n);
void main()
{
int i;
for(i=1; i<=4; i++)
cout << i << "! = " << fact(i) << endl;
}
int fact(int n)
{
static int f=1; //仅在第一次调用函数时执行一次
f *= n;
return f;
}
第 4章 函数程序运行结果:
1!=1
2!=2
3!=6
4!=24
4.8 变量的存储类别
4.8.2 变量的存储类别(续)
2,自动变量定义形式,auto 数据类型 变量名特点,定义变量时,若不指定 static或 auto,则默认为自动变量。自动变量是 动态存储方式 。
每次调用它所在的函数时,都要重新分配存储空间,并初始化。函数调用结束,存储空间就释放。
若不初始化,则初值是不确定的。
第 4章 函数例 4.17 静态变量与动态变量的使用
#include<iostream.h>
void other(void);
int i=1; // i 为全局变量,具有静态生存期。
void main(void)
{
static int a; // a为静态局部变量,具有全局寿命,局部可见。
int b=-10; // b,c为动态局部变量,具有局部生存期。
int c=0;
cout<<"---MAIN---\n";
cout<<" i,"<<i<<" a,"<<a<<" b,"<<b<<" c,"<<c<<endl;
c=c+8;
other();
cout<<"---MAIN---\n";
cout<<" i,"<<i<<" a,"<<a<<" b,"<<b<<" c,"<<c<<endl;
i=i+10;
other();
}
第 4章 函数例 4.17 静态变量与动态变量的使用 (续 )
void other(void)
{
// a,b为静态局部变量,具有全局寿命,局部可见,
// 只第一次进入函数时被初始化。
static int a=2;
static int b;
int c=10; // C为动态局部变量,每次进入函数时都初始化。
a=a+2;
i=i+32;
c=c+5;
cout<<"---OTHER---\n";
cout<<" i,"<<i<<" a,"<<a<<" b,"<<b<<" c,"<<c<<endl;
b=a;
}
第 4章 函数程序运行结果:
---MAIN---
i,1 a,0 b,-10 c,0
---OTHER---
i,33 a,4 b,0 c,15
---MAIN---
i,33 a,0 b,-10 c,8
---OTHER---
i,75 a,6 b,4 c,15
例 4.18 输出摄氏温度与华氏温度对照表分析:编写一个函数求出指定摄氏温度对应的华氏温度值,
在主函数中通过循环求出摄氏温度从 0~99度对应的华氏温度值,并输出。转换公式为:
F = 9/5*C +32
其中 F表示华氏温度,C为摄氏温度。程序如下:
#include <iostream.h>
#include <iomanip.h>
int Convert(int c)
{
int f;
f = (int) (9.0/5*c + 32);
return f;
}
第 4章 函数例 4.18(续一)
#void main()
{
cout << " | 0 1 2 3 4 5 6 7 8 9" << endl;
cout << "---|----------------------------------------------------------" << endl;
for(int i=0; i<10; i++)
{
cout << setw(2) << i*10 << " | ";
for(int j=0; j<10; j++)
{
cout << setw(3) << Convert(i*10+j) << " ";
}
cout << endl;
}
}
第 4章 函数例 4.18(续二)
程序运行结果:
第 4章 函数例 4.19 用递归的方法计算从 n个人中选取 k
个人的组合数分析:从 n个人中选取 k个人的组合数
=从 n-1个人中选取 k个人的组合数 + 从 n-1个人中选取 k-1个人的组合数当 n与 k相等,或 k等于 0时,组合数为 1,即有以下公式( comm
表示组合数):
1 n==k或 k==0
comm( n,k) =
comm(n-1,k)+ comm(n-1,k-1) n!=k 且 k>0
第 4章 函数例 4.19(续)
#include<iostream.h>
int comm(int n,int k);
void main(void)
{
int n,k;
cout << "请输入 n和 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);
}
第 4章 函数返 回程序运行结果:
请输入 n和 k的值,10 3
120
9.1 函数模板
1,问题的提出重载函数 可以解决功能相同或相似的函数 使用同一个函数名的问题。
void swap(char &x,char &y)
{
char t =x;
x = y;
y = t;
}
void swap(int &x,int &y)
{
int t =x;
x = y;
y = t;
}
第 9章 模板
void swap(float &x,float &y)
{
float t =x;
x = y;
y = t;
}
实际代码量并未减少。可使用函数模板减少大量代码。
9.1 函数模板
2,函数模板的定义
template <class T> 或类型名 函数名(参数表)
{
函数体
}
函数模板 就像是一个带有 类型参数 的函数 (参数 T即为类型 ),
编译程序会根据实际参数的类型确定参数的类型。
第 9章 模板
template <typename T>
类型名 函数名(参数表)
{
函数体
}
例 9.1 定义用于变量交换的函数模板
#include <iostream.h>
template <class T>
void swap(T &x,T &y)
{
T temp=x;
x=y;
y=temp;
}
void main(void)
{
char a='A',b='B';
int c=123,d=456;
double x=12.3,y=45.6;
swap(a,b);
swap(c,d);
swap(x,y);
cout << a << "," << b << endl;
cout << c << "," << d << endl;
cout << x << "," << y << endl;
}
第 9章 模板程序运行结果为:
B,A
456,123
45.6,12.3
T本身是一个类型参数,在调用函数 swap()时,编译程序会根据实际参数的类型确定 T的类型。
例 9.2 插入排序函数模板,使用插入排序函数模板可以为不同数据类型的数组排序,如整型、字符型、实型等等,为了使程序具有通用性,设计函数模板 InsertionSort()。
插入排序的基本思想:每一步将一个待排序的元素按其关键字值的大小插入到已排序序列的合适位置,直到待排序元素全部插入完为止 。
第 9章 模板例 9.2 (续一)
template <class T>
void InsertionSort(T A[],int n)
{
int i,j;
T temp;
for (i = 1; i < n; i++)
{
//从 A[i-1]开始向 A[0]方向扫描各元素,寻找适当位置插入 A[i]
j = i;
temp = A[i];
while (j > 0 && temp < A[j-1])
{
//当遇到 temp>=A[j-1]结束循环时,j便是应插入的位置
//当遇到 j==0结束循环时,则 0是应插入的位置。
A[j] = A[j-1]; //将元素逐个后移,以便找到插入位置时可立即插入。
j--;
}
A[j] = temp;
}
}
第 9章 模板例 9.2 (续二)
#include <iostream.h>
void main()
{
int a[10]={2,4,1,8,7,9,0,3,5,6};
double b[10]={12.1,24.2,15.5,81.7,2.7,5.9,40.3,33.3,25.6,4.6};
InsertionSort(a,10);
InsertionSort(b,10);
cout << a[0] << " " << a[1]<< " " << a[2] << " " << a[3] << " ";
cout << a[4] << " " << a[5]<< " " << a[6] << " " << a[7] << " ";
cout << a[8] << " " << a[9]<< endl;
cout << b[0] << " " << b[1]<< " " << b[2] << " " << b[3] << " ";
cout << b[4] << " " << b[5]<< " " << b[6] << " " << b[7] << " ";
cout << b[8] << " " << b[9]<< endl;
}
第 9章 模板程序运行结果为:
0 1 2 3 4 5 6 7 8 9
2.7 4.6 5.9 12.1 15.5 24.2 25.6 33.3 40.3 81.7
例 9.3 使用函数模板产生的二意性
#include <iostream.h>
template <class T>
T max(T a,T b)
{
return a>b?a:b;
}
void main(void)
{
int a = max(10.5,20);
double b = max(10,20.6);
cout << a << endl;
cout << b << endl;
}
第 9章 模板产生二意性,系统不能确定将其中的一个参数由整数转化为实数,还是应该将另一个参数由实数转化为整数 。
可使用强制类型转换解决:
int a =max( (int)10.5,20);
double b =max( (double ) 10,20.6);
返 回
3.使用显式规则和关键字 typename
C++ 专门定义一个仅仅在模板中使用的关键字 tpyename替代 class
使用显式方式调用,统一模板参数如,max<double>(10.5,20)
谢 谢!