C++大学基础教程第 4章 函数
2009-7-29 -2-
程序设计中,把具有一定功能的程序模块用函数或类来实现
2009-7-29 -3-
4.1 函数概述
4.2 函数定义
4.3 函数调用
4.4 内联函数
4.5 重载函数
4.6 默认参数值的函数
4.7 全局变量与局部变量
4.8 变量的存储类型
4.9 编译预处理第 4章 函数
4.1 函数概述
2009-7-29 -5-
4.1 函数概述
1.函数简介一般是将整个程序分为若干个程序模块每个模块用来实现一个特定的的功能
C++中模块的实现
函数
库函数
自定义函数

技巧:要熟悉 C++标准库提供的类和函数集合。不要事事从头做起,要尽可能利用 C++标准库提供的函数而不是生成新函数,
以便减少程序开发的时间。
2009-7-29 -6-
#include <iostream>
#include <cmath>
using namespace std;
int main() {
cout << "Enter Quadratic coefficients,";
double a,b,c;
cin >> a >> b >> c;
if ( (a != 0) && (b*b - 4*a*c > 0) )
{
double radical = sqrt(b*b - 4*a*c);
double root1 = (-b + radical) / (2*a);
double root2 = (-b - radical) / (2*a);
cout << "Roots," << root1 << " " << root2;
}
else
{
cout << "Does not have two real roots";
}
return 0;
}
调用函数 或主调函数被调函数库函数
2009-7-29 -7-
#include <iostream>
using namespace std;
float CircleArea(float r);
// main(),manage circle computation
int main() {
cout << "Enter radius,";
float MyRadius;
cin >> MyRadius;
float Area = CircleArea(MyRadius);
cout << "Circle has area " << Area;
return 0;
}
// CircleArea(),compute area of radius r circle
float CircleArea(float r) {
const float Pi = 3.1415;
return Pi * r * r;
}
自定义函数
2009-7-29 -8-
2.数学库函数
C++语言提供的库函数中有一些是专门完成特定的数学运算的,称为数学库函数。
实现常见的数学计算例如,求绝对值,平方根等 。
调用数学函数,函数名 (参数 1,…,参数 n)
例如,cout<<sqrt(900.0);
2009-7-29 -9-
2.数学库函数数学函数库中的多数函数都返回 double类型结果。
使用数学库函数,需要在程序中包含 math.h头文件,
这个头文件在新的 C++标准库中称为 cmath。
函数参数可取常量,变量或表达式 。
例,如果 c=13.0,d=3.0和 f=4.0,则下列语句,
cout<<sqrt(c+d*f);
计算并显示 13.0+3.0*4.0=25.0的平方根,即 5.0。
4.2 函数定义
2009-7-29 -11-
4.2 函数定义及使用
main()
function1() function2() function3()
function4() function5()
图4 - 1 层次化的函数关系
2009-7-29 -12-
4.2 函数定义及使用函数定义函数原型
return语句函数使用的三种方式
2009-7-29 -13-
1.函数的定义包括接口和函数体
接口
函数类型 函数名 形式参数表
函数体
完成函数功能的语句集合
返回值
2009-7-29 -14-
函数定义 语法形式函数类型 函数名 ( 形式参数表 )
{
函数体 ( 变量声明和语句 )
return 表达式 ;
}
2009-7-29 -15-
float CircleArea (float r) {
const float Pi = 3.1415;
return Pi * r * r;
}
函数定义函数体返回值语句局部变量定义形式参数函数类型 函数名
2009-7-29 -16-
函数名函数名是这个独立代码段(函数体)的外部标识符函数定义之后,即可通过函数名调用函数 (函数体代码段 )。
例,
cout << CircleArea(MyRadius) << endl;
函数名的构成可以是任何有效标识符
(一般多以反映函数功能的单词组合命名,以增强程序的可读性 )
2009-7-29 -17-
Sum()
// Sum(),compute sum of integers in a,.,b
int Sum(int a,int b) {
int Total = 0;
for (int i = a; i <= b; ++i) {
Total += i;
}
return Total;
}
2009-7-29 -18-
形式参数表函数的形式参数表,简称形参表形式:
(类型 1 形式参数 1,…,类型 n 形式参数 n)
形式参数表示主调函数和被调函数之间需要交换的信息
(1)传给被调函数的待处理的数据;
(2)控制被调函数操作执行的信息;
(3)被调函数执行的结果。
形式参数表从参数的类型、个数、排列顺序上规定了主调函数和被调函数之间信息交换的形式。
如果函数之间没有需要交换的信息,也可以没有形参,形参表内写 void或空着。
2009-7-29 -19-
Sum()
// Sum(),compute sum of integers in a,.,b
int Sum(int a,int b) {
int Total = 0;
for (int i = a; i <= b; ++i) {
Total += i;
}
return Total;
}
2009-7-29 -20-
PromptAndRead()
// PromptAndRead(),prompt and extract next
// integer
int PromptAndRead() {
cout << "Enter number (integer),";
int Response;
cin >> Response;
return Response;
}
2009-7-29 -21-
函数返回值函数返回值类型 规定了函数返回给主调函数的值的类型,也称为 函数类型 。
当需要函数向主调函数返回一个值时,可以使用
return语句,将需要返回的值返回给主调函数,故称之为 返回值 。
需要注意的是由 return语句返回的值的类型必须与函数定义时指定的函数返回值类型一致。
如果不需要向主调函数返回值,函数可以定义成无类型的,函数类型写成 void,函数结束时也不必用
return语句。
2009-7-29 -22-
Sum()
// Sum(),compute sum of integers in a,.,b
int Sum(int a,int b) {
int Total = 0;
for (int i = a; i <= b; ++i) {
Total += i;
}
return Total;
}
2009-7-29 -23-
函数体函数体 是实现函数功能的代码部分
变量声明
完成函数功能的语句两部分从组成结构看,函数体是由程序的三种基本控制结构即顺序、选择、循环结构组合而成的。
2009-7-29 -24-
Sum()
// Sum(),compute sum of integers in a,.,b
int Sum(int a,int b) {
int Total = 0;
for (int i = a; i <= b; ++i) {
Total += i;
}
return Total;
}
2009-7-29 -25-
函数是由函数名、函数类型、形参表和函数体四部分组成的,使用时通过函数名和参数表调用函数,
2009-7-29 -26-
例 4.1
编写一个函数 cube,计算整数的立方。调用函数
cube计算从 1到 10相邻整数的立方差。
2009-7-29 -27-
//计算整数的立方
#include <iostream>
using namespace std;
int cube( int ); // 函数原型声明
void main()
{
int last,nowcb;
last=1;
cout <<"the difference of cube,"<<endl;
2009-7-29 -28-
for ( int x = 2; x <= 10; x++ )
{
nowcb=cube( x );
cout <<nowcb-last << " ";
last=nowcb;
}
cout << endl;
}
2009-7-29 -29-
//函数定义
int cube( int y )
{
return y*y*y;
}
2009-7-29 -30-
例 4.2
在三个浮点中确定最大值,使用自定义函数
maximum完成。
2009-7-29 -31-
// 在三个浮点中找出最大值
#include <iostream>
using namespace std;
float maximum(float,float,float ); // 函数原型声明
void main()
{
float a,b,c;
cout << "Enter three floating numbers,";
cin >> a >> b >> c;
//调用 maximum函数,a,b,c为实际参数
cout << "Maximum is," << maximum( a,b,c ) << endl; //函数调用
}
2009-7-29 -32-
// maximum函数定义
// 函数的形式参数 x,y,z
float maximum(float x,float y,float z)
{
float max;
max = x>=y?x:y;
max = max>=z?max:z;
return max;
}
2009-7-29 -33-
2.函数原型引用函数之前,要先指定函数的接口形式
函数原型
函数定义函数原型声明格式,
函数类型 函数名 (形式参数表 );
例,int Max(int a,int b);
2009-7-29 -34-
函数原型函数原型声明 使编译器获得关于函数名称、函数类型、函数形参个数、形参类型和形参顺序的信息。
函数调用时,编译器根据函数原型声明验证函数调用正确与否。
2009-7-29 -35-
函数原型
库函数的声明在相应的库的头文件中,使用库函数时要包含相应的头文件。
例,#include <cmath>
调用数学库函数,
sqrt(…)
sin(…)
abs(…)
……
fstream
File stream processing
assert
C-based library for assertion
processing
iomanip
Formatted input/output (I/O)
requests
ctype
C-based library for character
manipulations
math
C-based library for trigonometric
and logarithmic functions
2009-7-29 -36-
函数原型
程序中,如果调用自定义的函数,且函数定义在后,
调用在先,则必须在调用函数之前有函数原型声明。
void subfun1( … ) ; //原型声明
main( )
{

sunfun1( … ) ; //函数调用

}
void subfun1( … ) //函数定义
{

}
2009-7-29 -37-
函数原型
如果是函数定义在先,调用在后,则不必进行函数原型声明。因为编译器已经从函数定义得到关于函数的信息。
void subfun1( … ) //函数定义
{

}
main( )
{

sunfun1( … ) ; //函数调用

}
2009-7-29 -38-
函数原型
源文件中,如果在所有函数定义体之外声明函数原型,则该函数可被位于其原型声明之后的所有函数调用。
2009-7-29 -39-
void subfun1( … ) ; //原型声明
main( )
{

sunfun1( … ) ; //函数调用

}
void sunfun2( )
{

sunfun1( … ) ; //函数调用

}
void subfun1( … ) //函数定义
{

}
main( )
{
void subfun1( … ) ; //函数原型声明

sunfun1( … ) ; //函数调用

}
void sunfun2( )
{

sunfun1( … ) ; //函数调用,┆
}
void subfun1( … )
{

}
错误,编译器不识别
sunfun1标识符。
2009-7-29 -40-
3,return语句
return语句使程序执行流程从被调函数返回主调函数,有两种形式:
( 1) 不返回值的形式:
return;
( 2) 返回值的形式
return 表达式;
2009-7-29 -41-
例 4.3
从键盘输入三角形的三个边长,计算三角形的面积。
2009-7-29 -42-
//给定三角形的三个边长,计算三角形的面积。
#include<iostream>
#include <cmath>
using namespace std;
void TriangleAreabySide(float a,float b,float c);
void main()
{
float a,b,c;
cout<<"input three numbers of the triangle sides:";
cin>>a>>b>>c;
TriangleAreabySide(a,b,c);
}
2009-7-29 -43-
//利用边长计算三角形的面积
void TriangleAreabySide(float a,float b,float c)
{
float area;
float s;
if (a+b<=c || a+c<=b || b+c<=a )
{
cout<<"Not a triangle!"<<endl;;
return;
}
2009-7-29 -44-
else
{
s=(a+b+c)/2;
area=sqrt(s*(s-a)*(s-b)*(s-c));
cout<<"area of the triangle("<<a<<","<<b<<","<<c<<"),
"<<area<<endl;
return;
}
}
2009-7-29 -45-
例 4.4
利用随机数产生函数 rand() 产生的随机数模拟考试成绩,统计成绩的平均值。
2009-7-29 -46-
//统计分数均值
#include<iostream>
#include<cstdlib>
#include <ctime>
using namespace std;
int CalMean(int count);
void main()
{
int count;//数据个数
int mean;//平 均值
2009-7-29 -47-
//输入数据
cout<<"input number:";
cin>>count;
mean=CalMean(count);
cout<<"mean= "<<mean<<endl;
}
2009-7-29 -48-
//计算均值
int CalMean(int count)
{
int score;//分数
int sum(0),mean;
srand( (unsigned)time( NULL ) );//种子
//累积求和
int k(0);
cout<<"the scores:"<<endl;
2009-7-29 -49-
while(k<count)
{
score=rand()%100;
if (score<10)
continue;//假设没有低于 10分的,舍弃此数据。
else
{
cout<<score<<" ";
sum+=score;//累积分数
k++;//累积个数
}
}
2009-7-29 -50-
cout<<endl;
//计算平均值
if(count>0)
mean=sum/count;
else
mean=0;
return mean;
}
2009-7-29 -51-
当被调函数只需要把一个数值结果返回给主调函数时,使用 return语句返回比较合适 。
如果使用 return语句给主调函数返回一个值,则
returnu语句必须返回一个与所在函数的函数类型一致的表达式 。 若表达式的结果与函数类型不一致,不能通过编译,需要作强制类型转换,将表达式的类型强制成与函数类型 。
例如,如果 mean是 float型,则 return语句应为:
return ( float) mean;
2009-7-29 -52-
4.函数使用的三种方式
(1) 函数语句函数语句形式:
函数名 ( 实参数表 ) ;
例如,TriangleAreabySide(a,b,c);
2009-7-29 -53-
实际参数表实际参数表,简称为 实参表实参表是按与被调函数形参表一一对应的格式组织的参数表,即参数的类型,个数和排列顺序必须与被调函数声明的形参数表严格一致 。
实际参数表的各实际参数以逗号间隔,实际参数可以是常量,变量和表达式 ( 常量和变量都是最简单的表达式 ) 。
在执行到函数调用语句时,由主调函数提供给被调函数的数据和控制信息的参数 ( 视为输入参数 ) 必须具有确定值 。
如果被调函数无形参,则实参表也是空的 。
实际参数以数据值 ( 值传递 ) 或实际存储空间 ( 地址传递 )
提供了形式参数所需的内容 。
实际编程中,从可读性考虑,一般使用变量(普通变量或指针变量)作实际参数。
2009-7-29 -54-
调用无参数的函数
#include<iostream>
using namespace std;
void DisplayMessage();
void main()
{
DisplayMessage (); //函数调用语句
}
void DisplayMessage ()
{
cout<<"只显示确定信息的简单函数不带参数 "<<endl;
}
2009-7-29 -55-
( 2)函数表达式
函数调用出现在一个表达式中,其形式:
函数名 ( 实际参数表 )
这种表达式称为函数表达式,由函数名和实参表组成。
此时函数要使用 return语句向主调函数返回一个确定的值,参与它所在的表达式的运算。
2009-7-29 -56-
例 4.5
编写程序,实现坐标旋转公式:


c oss i n
s i nc os
00
00
yxy
yxx


2009-7-29 -57-
//实现坐标旋转公式
#include<iostream>
#include <cmath>
using namespace std;
void main()
{
const double PI=3.14;
int x,y;//旋转后坐标
int x0,y0;//原始坐标
int angle; //旋转角度
2009-7-29 -58-
//输入数据
cout<<"input point(x,y):";
cin>>x0>>y0;
cout<<"input angle of rotation:";
cin>>angle;
//计算旋转后的坐标
double theta=angle*PI/180;
x=x0*cos(theta)-y0*sin(theta);
y=x0*sin(theta)+y0*cos(theta);
//输出结果
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
}
2009-7-29 -59-
注意:
当函数调用出现在表达式中时,函数一定要通过
return语句返回一个与函数类型一致的值,作为这个函数表达式的值参与相关计算。
2009-7-29 -60-
( 3)函数参数利用函数的返回值作实际参数,再作函数调用。
例如,m=max( a,max( b,c) ) ;
实质上也是函数表达式形式调用的一种 。
4.3 函数调用
2009-7-29 -62-
4.3 函数调用函数调用的执行机制值调用嵌套调用递归调用
2009-7-29 -63-
1.函数调用的执行机制系统在调用函数时,在特定内存空间中为函数建立一个 活动记录 。
函数的活动记录存储
在函数内定义的每个变量的值
主调函数的断点地址
被调函数的返回值
(保存信息的具体内容与所使用的编译器有关 )
活动记录是函数正常执行、调用和返回的物理基础。
2009-7-29 -64-
在程序执行过程中,如果遇到调用其它函数,则系统暂停当前函数的执行,将下一条指令的地址( 返回地址,当流程从被调函数返回当前函数后,从此地址继续执行当前函数的语句 。亦称 断点地址 )存入活动记录,流程转去执行被调函数。 系统为被调函数建立一个活动记录,被调函数的形参和在被调函数内定义变量将存入它的活动记录。 当执行完被调函数时,系统撤销它的活动记录,流程重新回到主调函数,恢复其断点处的运行状态,继续执行程序的后序语句,直至结束。
2009-7-29 -65-
void fun(… );
void main()
{

fun(… );

}
void fun(… )
{

return;
}
m a i n ( )
调f u n ( )
结束
f u n ( )
返回保存:
返回地址当前现场恢复:
主调函数现场返回地址
1
2
3
4
5
6
7
图4 - 2 函数调用和返回的过程
2009-7-29 -66-
例 4.6
从键盘输入屏幕上两点的坐标 (x,y),计算两点之间的距离。
(分析函数调用时活动记录 )
2009-7-29 -67-
//计算两点之间的距离
#include<iostream>
#include <cmath>
using namespace std;
float CalDistance(int x1,int y1,int x2,int y2);
int square(int x);
void main()
{
int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
2009-7-29 -68-
//输入数据
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
//计算距离
dist=CalDistance(x1,y1,x2,y2);
//输出结果
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2
<<","<<y2<<"),"<<dist<<endl;
}
2009-7-29 -69-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx2,dy2;
dx2=square(xx2-xx1);
dy2=square(yy2-yy1);
float dist=sqrt(dx2+dy2);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x*x;
}
2009-7-29 -70-
运行结果:
input point 1 (x1,y1):10 20
input point 2 (x2,y2):110 120
distance between point (10,20) and point (110,120),141.421
2009-7-29 -71-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
2009-7-29 -72-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
y1
x2
x1
y2
2009-7-29 -73-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
y1
x2
x1
y2
dist
2009-7-29 -74-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
y1
x2
x1
y2
dist
2009-7-29 -75-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
y1
x2
10x1
20
y2
dist
2009-7-29 -76-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
y1
x2
10x1
20
y2
dist
2009-7-29 -77-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
y1
x2
10x1
20
110
y2 120
dist
2009-7-29 -78-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x* x;
}
10xx1
20
110
120
yy1
xx2
yy2
2009-7-29 -79-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x* x;
}
10xx1
20
110
120
yy1
xx2
yy2
dx
dy
2009-7-29 -80-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x*x;
}
10xx1
20
110
120
yy1
xx2
yy2
dx
dy
2009-7-29 -81-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
100x
20
110
120
yy1
xx2
yy2
dx
dy
10xx1
2009-7-29 -82-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
100x
20
110
120
yy1
xx2
yy2
dx
dy
10xx1
2009-7-29 -83-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
20
110
120
10000
yy1
xx2
yy2
dx
dy
10xx1
2009-7-29 -84-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
20
110
120
10000
yy1
xx2
yy2
dx
dy
10xx1
2009-7-29 -85-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
100x
20
110
120
10000
yy1
xx2
yy2
dx
dy
10xx1
2009-7-29 -86-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
20
110
120
10000
10000
yy1
xx2
yy2
dx
dy
10xx1
2009-7-29 -87-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
20
110
120
10000
10000
yy1
xx2
yy2
dx
dy
141.421dist
10xx1
2009-7-29 -88-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx,dy;
dx=square(xx2-xx1);
dy=square(yy2-yy1);
float dist=sqrt(dx+dy);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x**x;
}
20
110
120
10000
10000
yy1
xx2
yy2
dx
dy
141.421dist
2009-7-29 -89-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
y1
x2
10x1
20
110
y2 120
141.421dist
2009-7-29 -90-
void main()
{ int x1,y1,x2,y2;//两点坐标
float dist; //两点间距离
cout<<"input point 1 (x1,y1):";
cin>>x1>>y1;
cout<<"input point 2 (x2,y2):";
cin>>x2>>y2;
dist=CalDistance(x1,y1,x2,y2);
cout<<"distance between point ("<<x1<<","<<y1<<") and
point ("<<x2<<","<<y2<<"),"<<dist<<endl;
}
2009-7-29 -91-
//计算距离
float CalDistance(int xx1,int yy1,int xx2,int yy2)
{
int dx2,dy2;
dx2=square(xx2-xx1);
dy2=square(yy2-yy1);
float dist=sqrt(dx2+dy2);
return dist;
}
//计算一个数的平方
int square(int x)
{
return x*
2009-7-29 -92-
返回值函数输入数据流输出数据流函数能够从输入数据或参数获得信息参数函数处理完的信息用返回值或输出数据流输出
2009-7-29 -93-
2,函数的参数传递(值调用)
函数之间的信息交换的一种重要形式是函数的参数传递,由函数的形式参数和实际参数实现 。
函数在没有被调用时,函数的形式参数并不占有实际的内存空间,也没有实际的值 。 C++语言中函数的参数传递方式分为两种:
·值传递
·地址传递
2009-7-29 -94-
值传递如果函数的形式参数为普通变量,当函数被调用时,系统为这些形式参数分配内存空间,并用实际参数值初始化对应的形式参数,形式上实际参数的值传递给了形式参数 。 这就是函数调用时参数的 值传递 。
值传递方式,实际参数和形式参数各自占有自己的内存空间; 参数传递方向只能由实际参数到形式参数;不论函数对形式参数作任何修改,对相应的实际参数都没有影响 。
2009-7-29 -95-
例 4.7
如果一个数的所有真因子 ( 包括 1,但不包括这个数本身 ) 之和正好等于这个数本身,则称此数为完美数 。 例如:
6=1× 2× 3,而 1+2+3=6;
28=1× 4× 7=1× 2× 14,而 1+2+4+7+14=28。
如何确定完美数,欧几里得发现,只要 2n-1是一个素数,则 2n-1(2n-1)一定是一个完美数 。 编写程序找出最小的 5个完美数 。
2009-7-29 -96-
//寻找最小的五个完美数
#include <iostream>
#include<cmath>
using namespace std;
bool DecidePrime(unsigned int number);
unsigned int power(unsigned int x,unsigned int y);
void main()
{
unsigned int perfect_number;
unsigned int num,temp;
short n(2);
short counter(0);//计数器
2009-7-29 -97-
while (counter<5)
{ temp=power(2,n);
num=temp-1;
if(DecidePrime(num))
{ perfect_number=temp/2*num;
cout<<"n="<<n<<","<<"perfect
number="<<perfect_number<<endl;
counter++;
}
n++;
}
}
2009-7-29 -98-
//计算指数
unsigned int power(unsigned int x,unsigned int y)
{
unsigned int mul(1);
for(int i=1;i<=y;i++)
mul*=x;
return mul;
}
2009-7-29 -99-
//判别素数
bool DecidePrime(unsigned int number)
{
unsigned int i,k;
k=sqrt(number);
for(i=2; i<=k; i++) //找 number的因数
{
if(number%i==0)
break;
}
2009-7-29 -100-
if(i>=k+1) //判断 number是否被小于 number的数整除
return true;
else
return false;
}
2009-7-29 -101-
例 4.8
从键盘输入两整数,交换次序后输出。
2009-7-29 -102-
//演示函数参数值传递单向性的例程
#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;
}
5
10
x
y
1024
1028
运行结果:
x=5 y=10
x=5 y=10
2009-7-29 -103-
void swap(int a,int b)
{
int t;
t=a;
a=b;
b=t;
}
5a 2048
10b 2052
t 20565
10
5
2009-7-29 -104-
3,嵌套调用
C++的函数不能 嵌套定义
C++的函数可以 嵌套调用
2009-7-29 -105-
例 4.9
编程计算一个空心圆柱体的体积。用函数
CylinderVolume() 计算一个半径为 r,高度为 h
的圆柱体的体积:
hr 2?
2009-7-29 -106-
//计算空心圆柱体的体积
#include <iostream>
#include <string>
using namespace std;
float DonutSize(float Outer,float Inner,float Width);
float CylinderVolume(float Radius,float Width);
2009-7-29 -107-
void main( )
{
cout<<"Outer edge donut radius:";
float OuterEdge;
cin>>OuterEdge;
cout<<"Hole radius,";
float InnerEdge;
cin>>InnerEdge;
cout<<"Donut thickness:";
float Thickness;
cin>>Thickness;
2009-7-29 -108-
//计算空心圆柱体的体积
cout<<endl<<"Size of donut with radius "<<
OuterEdge<<endl;
cout<<" hole radius "<<InnerEdge <<endl;
cout<<" thickness "<<Thickness<<endl;
cout<<" is "<<DonutSize(OuterEdge,InnerEdge,
Thickness)<<endl;
}
2009-7-29 -109-
//计算空心圆柱体体积
float DonutSize(float Outer,float Inner,float Width)
{
float OuterSize = CylinderVolume(Outer,Width);
float HoleSize = CylinderVolume(Inner,Width);
return OuterSize-HoleSize;
}
//计算圆柱体体积
float CylinderVolume(float r,float h)
{
const float pi=3.1415f;
return pi*r*r*h;
}
2009-7-29 -110-
函数m a i n ( )
调用函数
DonutSize
结束函数
DonutSize(...)
函数C y l i n d e r V o l u m e (,,,)
的第一次执行
1
2 3
4
5
6
10
图4 - 4 函数嵌套调用示意图调用函数
CylinderVolum
e
调用函数
CylinderVolum
e
7
函数C y l i n d e r V o l u m e (,,,)
的第二次执行8
9
11
12
13
2009-7-29 -111-
例 4.10
用弦割法求方程在 x=1.5 附近的根 。
0123 xx
2009-7-29 -112-
弦割法
2009-7-29 -113-
解:
分析:设,1)( 23 xxxf
)()(
)()(
01
0110
xfxf
xfxxfxx

2009-7-29 -114-
//用弦割法求方程的根。
#include<iostream>
#include<cmath>
using namespace std;
float function(float x);
float xIntersection(float x1,float x2);
float eqRoot(float x1,float x2);
2009-7-29 -115-
void main()
{ float x0,x1,x;
float y0,y1;
do
{ cout<<"enter x0(1.5),x1:"<<endl;
cin>>x0>>x1;
y0=function(x0);
y1=function(x1);
} while(y0*y1>=0);
x=eqRoot(x0,x1);
cout<<"root,"<<x<<endl;
}
2009-7-29 -116-
float function(float x)
{
float y= (x - 1)*x*x -1;
return y;
}
float xIntersection(float x0,float x1)
{
float y=(x0*function(x1)-x1*function(x0))/(function(x1)-
function(x0));
return y;
}
2009-7-29 -117-
float eqRoot(float x0,float x1)
{ float x,y,y0;
y0=function(x0);
do
{ x=xIntersection(x0,x1);
y=function(x);
if( y*y0>0)
{ y0=y;
x0=x;
}
else x1=x;
}while(fabs(y)>=0.000001);
return x;
}
2009-7-29 -118-
4,递归调用递归函数 是函数体内有调用函数自己的语句或通过其它函数间接调用函数自己的函数。
递归调用 是调用递归函数而形成的一种函数调用方式。
递归函数用于实现数学中的递归问题比较直观方便。
2009-7-29 -119-
经典递归调用问题阶乘定义式:

)!1(
1
!
nn
n 0n
0n若

2009-7-29 -120-
int factorial( int n)
{
int fn
if(n==0)
fn = 1;
else
fn=n*factorial(n-1);
return fn;
}
2009-7-29 -121-
#include <iostream>
using namespace std;
void main()
{
cout<<"enter a position integer:";
int n,n_fact;
cin>>n;
n_fact=factorial(n);
cout<<"the factorial of "<<n<<"is,"<<n_fact<<endl;
}
2009-7-29 -122-
main()
调用函数
factorial(3)
1
n = 3
n_fact=?
17
结束
n = 3
n_fact=6
第一次调用函数
factorial(int n)
2
调用函数
factorial(2)
3
n = 3
fn=?
第二次调用函数
factorial(int n)
4
5
n = 2
fn=?
调用函数
factorial(1)
第三次调用函数
factorial(int n)
第四次调用函数
factorial(int n)
调用函数
factorial(0)
7
n = 1
fn=?
6 8
n = 0
fn=1
9
10
11
n = 1
fn=1
14 12
16 1315
n = 2
fn=2
n = 3
fn=6
n_fact=返回 值 fn =3 *返 回值 fn =2 *返 回值 fn =1 *返 回值
return fn
return fnreturn fnreturn fn
图4,5 fa ct or ia l函 数递归调用过程示意图
2009-7-29 -123-
递归问题的求解实际分成两个阶段:
化简问题的递推阶段;
达到递归终止条件得到基本情况的结果,并逐步回推结果阶段 。
递归函数的主要两部分
具有更简单参数的递归调用
停止递归的终止条件 ( 递归终止条件 )
2009-7-29 -124-
例 4.11
整数 x和 y的最大公约数是 x和 y能够整除的最大整数。编写一个递归函数 GcommonDivisor,返回整数 x和 y的最大公约数。
整数 x和 y的最大公约数的递归定义如下:如果 y等于 0,则 GcommonDivisor( x,y) 为 x,否则
GcommonDivisor( x,y) 为 GcommonDivisor( y,
x%y),其中 %是求模运算符。
2009-7-29 -125-
//计算 x和 y的最大公约数 (递归函数 )
#include<iostream>
using namespace std;
int GcommonDivisor(int x,int y);
void main()
{ int x,y;
cout<<"input x,y:";
cin>>x>>y;
cout<<"the greatest common divisor of "<<x<<"and "<<y<<","
<<GcommonDivisor(x,y)<<endl;
}
2009-7-29 -126-
int GcommonDivisor(int x,int y)
{
if (y==0)
return x;
else
return GcommonDivisor(y,x%y);
}
2009-7-29 -127-
例 4.12
汉诺塔问题汉诺塔问题是著名的经典问题之一 。 传说远东地区有一座庙,僧人要把盘子 ( 64个 ) 从第一个柱 (A)移到第三个柱 (C),这 64个盘子从上至下逐渐增大 。 僧人移动盘子的规则是:每次只能移动一个盘子;柱子上任何时候都要保持大盘在下,小盘在上的放臵方式;移动过程中,可以借助于第二个柱 ( B),暂时放臵盘子 。 据传说,僧人们完成这个任务时,世界的末日就来临了 。 十九世纪法国数学家鲁卡斯指出,完成这个任务,僧人们移动盘子的总次数为,。 假设一秒钟移动一个,每天 24小时不停地移动,需要 5800亿年 。
1264?
A B C
2009-7-29 -128-
解:
分析,
这个问题适合用递归法解决,递归函数为 hanoi()。 设 A柱上需要移动的盘子的个数为 n,移动 n个盘子可以利用递归法降阶为移动 n-1个盘子,具体步骤为:
步骤 1.如果 n=1,则盘子直接从 A柱到 C柱,A-->C; 执行步骤 3;
否则执行步骤 2。
步骤 2.如果 n>1,则将盘子分成最大的 1个和其它 ( n-1) 个,先把 ( n-1) 移到 B柱上,再把最大的盘直接移到 C柱上,然后把 B
柱上的 n-1个盘移到 C柱上 。
2009-7-29 -129-
即:
① 将 A柱上 (n-1)个盘子:借助 C柱,由 A柱移到 B柱,hanoi(n-
1,A,C,B);
② 将最大的一个盘子由 A柱移到 C柱,move(A,C);
③ 再将 B柱上 (n-1)个盘子借助 A柱移到 C柱上,hanoi(n-
1,B,A,C)。
步骤 3.结束 。
其中,函数 move()为直接移动盘子函数。
2009-7-29 -130-
//汉诺塔问题程序
#include"iostream"
using namespace std;
void move(char x,char z);
void hanoi(int n,char x,char y,char z);
2009-7-29 -131-
void main()
{
int num;
cout<<"请输入盘子数,";
cin>>num;
cout<<"按 \"汉诺塔 \"的规则,把 "<<num
<<"个盘子从 a针搬到 c针的步骤是,"<<endl;
hanoi(num,'A','B','C');
}
2009-7-29 -132-
void move(char x,char z)
{
static int i;
i++;
cout<<i<<","<<x<<" → "<<z<<endl;
}
2009-7-29 -133-
void hanoi(int n,char x,char y,char z)
(1){
(2) if(n==1)
(3) move(x,z);
(4) else {
(5) hanoi(n-1,x,z,y);
(6) move(x,z);
(7) hanoi(n-1,y,x,z);
(8) }
(9)}
递归运行的层次运行语句行号递归工作栈状态
(返址,n值,
x值,y值,z值 )
1 1,2,4,5 0,2,a,b,c
2 1,2,3,9 6,1,a,c,b
0,2,a,b,c
1 6 0,2,a,b,c
2 1,2,3,9 8,1,b,a,c
0,2,a,b,c
1 8,9 0,2,a,b,c
0 栈空
4.4 内联函数
2009-7-29 -135-
4.4 内联函数函数调用时,系统首先要保存主调函数的相关信息,再将控制转入被调函数,这些操作增加了程序执行的时间开销。
C++提供的内联函数形式可以减少函数调用的额外开销 (时间空间开销 ),特别是一些常用的短小的函数适合采用内联函数形式。
2009-7-29 -136-
内联函数内联函数的定义形式:
inline 函数类型 函数名 ( 形式参数表 )
{
函数体
}
2009-7-29 -137-
例 4.13
使用内联函数求三个整数中的最大值。
2009-7-29 -138-
//内联函数例
#include<iostream>
using namespace std;
inline int max(int x,int y,int z)
{
return ((x>=y)? (x>=z? x,z),(y>=z? y,z));
}
2009-7-29 -139-
void main()
{
int a,b,c;
cout<<"enter three integers:"
cin>>a>>b>>c;
cout<<"Maximum is "<<max(a,b,c)<<endl;
}
2009-7-29 -140-
内联函数之所以能够减少函数调用时的系统空间和时间开销,是因为系统在编译程序时就已经把内联函数的函数体代码插入到相应的函数调用位臵,成为主调函数内的一段代码,可以直接执行,不必再转换流程控制权 。
这样的结构,自然节省了时间和空间开销,但使得主调函数代码变长 。
一般是只把短小的函数写成内联函数 。
2009-7-29 -141-
注意:
( 1) 内联函数体不能包含循环语句,switch语句和异常接口声明 。
( 2) 内联函数要先定义,后调用 。 因为编译器需要用内联函数的函数体代码替换对应的函数调用 。
如果内联函数不符合要求,编译器就将内联函数当一般函数处理 。
4.5 重载函数
2009-7-29 -143-
4.5 重载函数重载函数也是函数的一种特殊情况。
C++允许几个功能类似函数同名,但这些同名函数的形式参数必须不同,称这些同名函数为重载函数 。
例,
int max(int x,int y){return x>y?x:y; }
float max(float x,float y) {return x>y?x:y; }
2009-7-29 -144-
各重载函数形式参数的不同是指参数的个数,类型或顺序彼此不同,不包括参数标识符的不同 。 如:
① int max(int a,int b){return a>b?a:b;}
② int max(int x,int y){return x>y?x:y; }
③ int max(int x,int y,int z)
{return (x>y?x:y)>z? (x>y?x:y):z; }
①② 实际是一个函数,如果写在同一个文件中,编译时会出现编译错误。若①③或②③在同一个文件中可形成重载函数。编译器将以形式参数个数的不同来认定和区分重载函数。
2009-7-29 -145-
#include<iostream>
using namespace std;
int min(int x,int y)
{
return x<y?x:y;
}
double min(double x,double y)
{
return x<y?x:y;
}
2009-7-29 -146-
void main()
{
int ia(10),ib(20);
double da(0.1),db(0.5);
cout<<"minimum of integer,is "<<min(ia,ib)<<endl;
cout<<"minimum of double is "<<min(da,db)<<endl;
}
2009-7-29 -147-
在使用重载函数时需要注意下面三点:
(1) 编译器不以形式参数的标识符区分重载函数 。 例
int max(int a,int b);
int max(int x,int y);
编译器认为这是同一个函数声明两次,编译时出错 。
(2) 编译器不以函数类型区分重载函数 。
float fun(int x,int y);
int fun(int x,int y);
如果函数名和形式参数表相同,只是函数类型不同,编译器同样认为它们是同一个函数声明两次,编译出错 。
(3) 不应该将完成不同功能的函数写成重载函数,破坏程序的可读性 。
2009-7-29 -148-
重载函数常用于实现功能类似而所处理的数据类型不同的问题,
4.6 默认参数值的函数
2009-7-29 -150-
4.6 默认参数值的函数具有默认参数值的函数 是一种特殊的函数形式,
C++允许函数的形式参数有默认值。
例如,计算圆面积的函数:
double CircleArea(double radius=1.0)
{
const double PI=3.14;
return PI*radius*radius;
}
2009-7-29 -151-
调用具有默认参数值的函数时,如果提供实际参数值,
则函数的形参值取自实际参数;如果不提供实际参数值,函 数 的 形 参 采 用 默 认 参 数 值 。 例 如 调 用
CircleArea函数:
#include<iostream>
using namespace std;
void main()
{
cout<<CircleArea(10.0)<<endl; //提供实际参数值
cout<<CircleArea()<<endl; //不提供实际参数值
}
2009-7-29 -152-
默认参数值函数如果有多个参数,而其中只有部分参数具有默认值,则这些具有默认值的参数值应该位于形参表的最右端 。 或者说,形参表中具有默认参数值的参数的右边,不能出现没有默认值的参数 。 例如:
int CuboidVolume(int length=1,int width=1,int height=1); //正确
int CuboidVolume(int length,int width=1,int height=1); //正确
int CuboidVolume(int length,int width,int height=1); //正确
int CuboidVolume(int length=1,int width,int height=1); //错误
int CuboidVolume(int length,int width=1,int height); //错误
int CuboidVolume(int length=1,int width=1,int height); //错误
2009-7-29 -153-
如果默认参数值函数是先声明,后定义的,则在声明函数原型时就指定默认参数值。
如果函数定义在先 (无需原型声明 ),则在函数定义的形参表中指定默认值。
2009-7-29 -154-
例 4.14
编写具有默认函数值的函数,计算直角三角形的面积。
2009-7-29 -155-
//使用默认形式参数值的函数编写计算直角三角形面积的程序
#include <iostream>
using namespace std;
float areaRATriangle( int side1 = 3,int side2 = 4);
void main()
{ cout<< "The area of default right-angled triangle(3,4) is:,
<< areaRATriangle()<<endl;
cout << "The area of right-angled triangle(6,4) is:“
<< areaRATriangle(6)<<endl;
cout<< "The area of right-angled triangle(6,8) is:"
<< areaRATriangle(6,8)<< endl;
}
2009-7-29 -156-
// 计算直角三角形面积
float areaRATriangle( int side1,int side2)
{
return side1* side2 /2.0;
}
4.7 全局变量和局部变量
2009-7-29 -158-
4.7 全局变量与局部变量在程序中,根据变量定义的位臵编译器把它们分为局部变量和全局变量。在函数内部定义的变量、函数的形式参数为 局部变量,在函数外部定义的变量为 全局变量 。
由于变量定义的位臵不同,所以变量起作用的范围也不同。我们把程序中一个标识符起作用的范围称为其 作用域 。
2009-7-29 -159-
局部变量局部变量 包括在函数体内定义的变量和函数的形式参数,它们的作用域就在函数体内,只能在本函数内使用,不能被其它函数直接访问。
2009-7-29 -160-
#include <iostream>
using namespace std;
int fun1(int x,int y);
int fun2(int x,int y);
void main()
{
int a,b;
cout<<"input a,b:"
cin>>a>>b;
cout<<fun1(a,b) <<endl;
cout <<fun2(a,b) <<endl;
}
2009-7-29 -161-
int fun1(int x,int y)
{
int n;
n=x*x+y*y;
return n;
}
int fun2(int x,int y)
{
int m;
m=x*x-y*y;
return m;
}
2009-7-29 -162-
局部变量能够随其所在的函数被调用而被分配内存空间,也随其所在的函数调用结束而消失 (释放内存空间 ),所以使用这种局部变量能够提高内存利用率 。
同时,由于局部变量只能被其所在的函数访问,所以这种变量的数据安全性也比较好 (不能被其它函数直接读写 )。 局部变量在我们实际编程中使用频率最高 。
2009-7-29 -163-
全局变量一个 C++的源文件(,cpp) 可以由多个函数组成,我们可以在函数外部定义变量,即全局变量。全局变量能够被位于其定义位臵之后的所有函数(属于本源文件的)共用。也就是说全局变量起作用的范围是从它定义的位臵开始至源文件结束。
全局变量的作用域是整个源文件。
2009-7-29 -164-
#include<iostream>
using namespace std;
int maximum;
int minimum;
void fun(int x,int y int z)
{
int t;
t=x>y?x:y;
maximum=t>z?t:z;
t=x<y?x:y;
minimum=t<z?t:z;
}
2009-7-29 -165-
void main()
{
int a,b;
cout<<"input data a,b:";
cin>>a>>b;
fun(a,b);
cout<<"maximum="<<maximum<<endl;
cout<<"minimum="<<minimum<<endl;
}
2009-7-29 -166-
全局变量在程序执行的整个过程中,始终位于全局数据区内固定的内存单元;如果程序没有初始化全局变量,系统会将其初始化为 0。
在定义全局变量的程序中,全局变量可以被位于其定义之后的所有函数使用 ( 数据共享 ),这给编程者带来很大方便;但也因此带来数据安全性和程序可读性不好的缺点 。 在我们实际编程时一般不要随意使用全局变量 。
2009-7-29 -167-
作用域程序中标识符的作用域也就是标识符起作用的范围,标识符只能在其起作用的范围内被使用。
从标识符起作用的范围上划分,作用域主要分为全局作用域和局部作用域两种。从标识符在程序中所处的位臵,又可区分作用域为块作用域、函数作用域、类作用域和文件作用域。
2009-7-29 -168-
块作用域块作用域是指标识符起作用的范围为块内范围,
在块内定义的标识符具有块作用域。这个块,可以是复合语句的块,也可以是函数定义的函数体块。块内定义的局部变量的作用域是从变量定义起至本块结束。
2009-7-29 -169-
#include<iostream>
using namespace std;
void main()
{ int a,b;
cout<<"input a,b:"
cin>>a>>b;
if (a<b)
{ int t;
t=a;
a=b;
b=t;
}
int c=a*a-b*b;
cout<<a<<"*"<<a" - "<<b<<"*"<<b
<<"="<<c<<endl;
}
a和 b的作用域
t的作用域
c的作用域
2009-7-29 -170-
函数作用域函数作用域
语句标号标识符的作用域为函数范围
函数体内定义的语句标号标识符具有函数作用域,
即语句标号的作用域是其所在的函数范围 。
2009-7-29 -171-
文件作用域文件作用域 也即全局作用域,指标识符的作用域为文件范围。
在源文件所有函数之外声明或定义的标识符具有文件作用域,全局变量和函数名 (不包括在其它函数内部声明原型的函数名 )具有全局作用域,起作用的范围是从声明或定义点开始,
直至其所在文件结束。
2009-7-29 -172-
#include<iostream>
using namespace std;
int counter1,counter2,counter3;
char color;
inline void getColor()
{
cout<<"input color of ball(r-red,y-yellow,g-green):";
cin>>color;
}
2009-7-29 -173-
void displayResult ()
{
switch(color) {
case 'r':
counter1++;
cout<<"The number of red balls,"<<counter1<<endl;
break;
case 'y':
counter2++;
cout<<"The number of yellow balls,"<<counter1<<endl;
break;
2009-7-29 -174-
case 'g':
counter3++;
cout<<"The number of green balls,"<<counter1<<endl;
break;
default:
break;
}
}
2009-7-29 -175-
void main()
{
for(int k=0;k<10;k++)
{
getColor();
displayResult();
}
}
2009-7-29 -176-
4.可见性标识符的 可见性,是研究标识符在其作用域内能否被访问到的问题。
标识符在其作用域内,能被访问到的位置称其为 可见的,
不能被访问到的位置称其为 不可见的 。
2009-7-29 -177-
#include<iostream>
using namespace std;
double pi=3.1415926;
double BallVolume(double radius)
{
double volume=pi*radius*radius*radius*4/3;
//此处使用的是全局变量 pi
return volume;
}
2009-7-29 -178-
double CircleArea(double radius)
{
double area=pi*radius*radius; //此处使用的是全局变量 pi
return area;
}
float CircleArea( float radius)
{
float pi=3.14f;
float area=pi*radius*radius;
//此处使用的是局部变量 pi,全局变量 pi在此处不可见。
return area;
}
2009-7-29 -179-
void main()
{
float r;
cout<<"input radius,";
cin>>r;
double dr=(double) r;
cout<<"Ball volume,"<<BallVolume(dr)<<endl;
cout<<"Circle area,"<<CircleArea(dr)<<"(double)"<<endl;
cout<<"Circle area,"<<CircleArea(r)<<"(float)"<<endl;
}
2009-7-29 -180-
C++规定:内层标识符与外层标识符同名时,内层标识符可见,外层标识符不可见 。 对于变量也即内层变量屏蔽外层同名变量 。
2009-7-29 -181-
如果函数内的局部变量与全局变量同名,且在函数内一定要使用这个同名全局变量,可以用全局作用域符号(,:)指定要访问的全局变量。
2009-7-29 -182-
#include<iostream>
using namespace std;
double pi=3.1415926;
void BallVolume(double radius)
{
double volume=pi*radius*radius*radius*4/3;
//此处使用的是全局变量 pi
cout<<"Ball volume,"<<volume<<endl;
}
2009-7-29 -183-
void CircleArea( float radius)
{
float pi=3.14f;
float area=pi*radius*radius;
//此处使用的是局部变量 pi,全局变量 pi在此处不可见。
cout<<"Circle area,"<<area<<"(float)"<<endl;
area=::pi*radius*radius; //此处使用的全局变量 pi
cout<<"Circle area,"<<area<<"(double)"<<endl;
}
2009-7-29 -184-
void main()
{
float r;
cout<<"input radius,";
cin>>r;
BallVolume(r);
CircleArea(r);
}
2009-7-29 -185-
关于标识符的使用
(1)标项识符应该先声明,后使用 。
(2)在同一作用域中,不能声明同名的标识符 。
(3)对于两个嵌套的作用域,如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见;如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见。
4.8 变量的存储类型
2009-7-29 -187-
4.8 变量的存储类型一个变量在内存中存在的时间取决于变量的存储类型
C++程序中使用的变量可分为四种存储类型:
auto
register
Extern
static
2009-7-29 -188-
auto型变量
auto型变量包括函数体内部定义的局部变量、
函数的形式参数,称为自动变量。
2009-7-29 -189-
#include<iostream>
using namespace std;
int max(int x,int y) { return x>y?x:y; }
void main()
{
int a,b;
cout<<"input a,b:";
cin>>a>>b;
cout<<max(a,b)<<endl;
}
auto int a,b;
2009-7-29 -190-
自动变量因其所在的函数被调用而产生,随其所在的函数调用结束而消失。因为自动变量存于动态存储区,不长时间占据固定内存,有利于内存资源的动态使用,
故程序中大量使用的都是自动变量。
2009-7-29 -191-
register型变量寄存器型变量定义格式:
register 类型标识符 变量标识符 ;
例,
register int counter;
访问寄存器中的变量要比访问内存中的变量速度快,但由于寄存器数量有限,如果设臵过多的
register型变量,编译器将把这些变量按普通局部变量处理,依然放在内存空间
2009-7-29 -192-
extern关键字多个源文件程序结构在多文件程序结构中,如果一个文件中的函数需要使用其它文件里定义的全局变量,可以用
extern关键字声明所要用的全局变量 。
//file1.cpp
int x,y;
void main()
{

}
//file2.cpp
extern int x,y;
void fun()
{

}
2009-7-29 -193-
extern关键字关键字 extern提供了多文件程序结构中不同源文件共享数据的一个途径 。 但实际编程中,共享数据时要注意数据的安全性问题 。
2009-7-29 -194-
静态变量声明变量时加上关键字 static,则该变量为静态变量,定义格式:
static 类型标识符 变量标识符 ;
静态变量
静态局部变量
static加在局部变量的定义前,则生成静态局部变量
静态全局变量
static加在全局变量定义前,则形成静态全局变量 。
静态变量在程序运行期间一直在静态存储区占有固定的存储空间 。
2009-7-29 -195-
#include<iostream>
using namespace std;
int squareMean(int data)
{
static int sum(0);
static int counter(0);
sum+=data*data;
counter++;
return sum/counter;
}
2009-7-29 -196-
void main()
{
int number(1);
while(number!=-1)
{
cout<<"input number:";
cin>>number;
cout<<"the mean of square is:"<<
squareMean(number)<<endl;
}
}
2009-7-29 -197-
静态局部变量在其所在的函数第一次被调用时,
被初始化为一定的值,系统仅对它们作一次初始化。
如果程序中指定初始化值,则初始化为程序指定值;
如果程序在定义它们时未指定初始值,则系统将静态局部变量初始化为 0。此后静态局部变量能够保持其在前一次函数调用结束时所获得的值,直到下次函数调用时被修改。
2009-7-29 -198-
静态全局变量只能在其定义文件中使用,不能被多文件程序结构的其它文件访问 。 除此之外,静态全局变量在定义它的文件中的用法与前面介绍的不加
static的全局变量一样使用 。 静态全局变量的数据安全性好于普通全局变量,但不便于多文件程序结构不同文件之间的数据共享,实际编程时要根据具体问题决定是否加 static。
2009-7-29 -199-
生存期一个变量在内存中存在的时间为变量的 生存期 。
不同的存储类型的变量的生存期不同,按生存期可以将变量分为两种:
静态生存期变量
全局变量,静态变量
动态生存期变量
auto型变量,register型变量
2009-7-29 -200-
生存期具有静态生存期的变量在程序运行期间一直存在 。
具有动态生存期的变量的取决于所在的函数是否被调用,
函数被调用,动态生存期的变量存在;函数调用结束,
动态生存期变量消失 。
具有静态生存期的变量,如果定义时未指定初始值,则系统将它们初始化为 0;
具有动态生存期的变量,如果未作初始化,则为随机值 。
在循环结构中,使用具有动态生存期的变量时,要特别注意是否需要先赋值的问题,例如迭代求和 ( 和变量初始化为 0) 或乘积 ( 积变量初始化为 1) 等 。
2009-7-29 -201-
多文件结构用 C++编写处理比较复杂的问题的程序,一般采用多文件结构程序,即由多个源程序分别完成不同的子功能,这样的程序组织便于管理和维护 。
C++既支持面向过程的程序设计,也支持面向对象的程序设计 。
2009-7-29 -202-
多文件结构在面向过程的程序设计中,为方便开发和维护程序,将程序的功能分成相对独立的子功能,然后用不同的源程序分别实现各个子功能 。 在实现每个子功能时,一般可使用两个源文件:一个是包含程序自定义类型,符号常量定义和函数的声明等的头文件 (*.h文件 ),一个是由实现算法的函数构成的,cpp文件 ( 即由函数定义构成的文件 ) 。
2009-7-29 -203-
加入多文件结构图
2009-7-29 -204-
例 4.15
模拟投币的程序,每次结果应为正面或反面,
打印 HEADS或 TAILS。 让程序投币 100次,计算每面出现的次数并打印结果。程序应调用一个 Flip
函数,该函数无参数,返回 0表示正面,1表示反面。如果程序真实,模拟投币,则每一面出现的次数应近似相等。
2009-7-29 -205-
//Mainprog.cpp
//模拟投币程序
#include<iostream>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include"flip.h"
using namespace std;
2009-7-29 -206-
void main()
{
cout<<"now let's begin:"<<endl;
int k(1);
int Hcounter(0),Tcounter(0);
srand( (unsigned)time( NULL ) );
2009-7-29 -207-
while(k<=100)
{ if(!Flip())
{
cout<<"HEADS(k="<<k<<") ";
Hcounter++;
}
else
{ cout<<"TAILS(k="<<k<<") ";
Tcounter++;
}
if(k%4==0)
cout<<endl;
k++;
}
2009-7-29 -208-
cout<<"total:"<<k-1<<endl;
cout<<"HEADS:"<<Hcounter<<endl;
cout<<"TAILS:"<<Tcounter<<endl;
}
2009-7-29 -209-
//Flip.cpp
//投币
#include <cstdlib>
#include"flip.h"
int Flip()
{
return rand()%2;
}
//flip.h
int Flip();
2009-7-29 -210-
在多文件结构程序中,函数的声明和函数定义、使用分别放在 *.h文件和 *.cpp文件中,使用时要在 cpp文件的最开始使用 include将要用的头文件包含进来。
4.9 编译预处理
2009-7-29 -212-
4.9编译预处理编译器在编译源程序之前,先由预处理器处理预处理指令
2009-7-29 -213-
预处理命令 #include
格式
#include<头文件名 >
#include“头文件名,
功能将一个头文件嵌入 (包含 )到当前文件
2009-7-29 -214-
预处理命令 #define
格式
#define 标识符 字符串功能把字符串命名为标识符 (用标识符代表字符串 )
标识符可以表示符号常量或宏名,编写源程序时代替,字符串,出现在程序中,编译时又被替换为,字符串,内容。
2009-7-29 -215-
预处理命令 #undef
格式
#undef 标识符功能撤销前面用 #define定义的标识符
2009-7-29 -216-
预处理命令 #ifdef
格式
#ifdef 标识符语句
#endif
功能如果已定义了,标识符,,则编译,语句,
2009-7-29 -217-
预处理命令 #ifndef
格式
#ifndef 标识符语句
#endif
功能如果未定义了,标识符,,则编译,语句,
2009-7-29 -218-
预处理命令
#if #elif #else #endif
功能条件编译指令,如果某个表达式成立,则编译相应的语句 。 几种形式是,
#if-#endif、
#if-#else-#endif、
#if-#elif-#elif-… #endif
2009-7-29 -219-
预处理命令
#if #elif #else #endif
格式
#if 常量表达式语句
#endif
2009-7-29 -220-
预处理命令
#if #elif #else #endif
格式或,
#if 常量表达式 1
语句 1
#else
语句 2
#endif
2009-7-29 -221-
预处理命令
#if #elif #else #endif
格式或,
#if 常量表达式 1
语句 1
#elif常量表达式 2
语句 2

#else
语句 n
#endif
2009-7-29 -222-
例 4.16
从键盘输入数据完成复数运算 (加、减、乘、除、
模、辐角 )。
2009-7-29 -223-
//myFile.cpp
#include<iostream>
using namespace std;
#include "complex.h"
void main()
{
char ch;
do
{
displayMessage();
cin>>ch;
2009-7-29 -224-
switch(ch)
{
case '1',Add();
break;
case '2',Minus();
break;
case '3',Multiply();
break;
case '4',Divide();
break;
case '5',Modul();
break;
2009-7-29 -225-
case '6',amplitude();
break;
default:
break;
}
cout>>"continue?(y/n)"
cin>>ch;
}while(ch=='y');
}
2009-7-29 -226-
inline void displayOperation()
{
cout<<"Enter your choice:">>endl;
cout<<"add(1)">>endl;
cout<<"minus(2)">>endl;
cout<<"multiply(3)">>endl;
cout<<"divide(4)">>endl;
cout<<"modul(5)">>endl;
cout<<"amplitude(6)">>endl;
}
2009-7-29 -227-
//complex.h
#ifndef COMPLEX_H //防止此文件被多次嵌入
#define COMPLEX_H
#define DEBUG_COMPLEX //调试时为显示某些中间结果; //在最终版本,
如果不需要,可将此句注释掉。
#define PI 3.14 //定义符号常量 PI,用 PI表示常量 3.14
#define ANGLE180 180 //定义符号常量
void Add();
void Minus();
void Multiply();
void Divide();
void Modul();
void amplitude();
#endif
2009-7-29 -228-
//complex.cpp
#include<iostream>
#include<cmath>
#include"complex.h"
using namespace std;
void Add()
{
double real1,real2,imag1,imag2;
cout<<"input real part and imaginary part of 1rd complex
number:";
cin>>real1>>imag1;
cout<<"input real part and imaginary part of 2rd complex
number:";
2009-7-29 -229-
cin>>real2>>imag2;
double real,imag;
real=real1+real2;
imag=imag1+imag2;
cout<<"sum:"<<real<<"+"<<imag<<"i"<<endl;
}
2009-7-29 -230-
void Minus()
{ double real1,real2,imag1,imag2;
cout<<"input real part and imaginary part of 1rd complex
number:";
cin>>real1>>imag1;
cout<<"input real part and imaginary part of 2rd complex
number:";
cin>>real2>>imag2;
double real,imag;
real=real1-real2;
imag=imag1-imag2;
cout<<" difference:"<<real<<"+"<<imag<<"i"<<endl;
}
2009-7-29 -231-
void Multiply()
{ double real1,real2,imag1,imag2;
cout<<"input real part and imaginary part of 1rd complex
number:";
cin>>real1>>imag1;
cout<<"input real part and imaginary part of 2rd complex
number:";
cin>>real2>>imag2;
double real,imag;
real=real1*real2-imag1*imag2;
imag=imag1*real2+imag2*real1;
cout<<" product:"<<real<<"+"<<imag<<"i"<<endl;
}
2009-7-29 -232-
void Divide()
{ double real1,real2,imag1,imag2;
cout<<"input real part and imaginary part of 1rd complex
number:";
cin>>real1>>imag1;
cout<<"input real part and imaginary part of 2rd complex
number:";
cin>>real2>>imag2;
double real,imag;
double temp;
temp=real2*real2+imag2*imag2;
2009-7-29 -233-
#ifdef DEBUG_COMPLEX
cout<<"temp="<<temp; //调试版本显示 temp,便于分析错误
#endif
if(temp>0.000001)
{
real=(real1*real2+imag1*imag2)/temp;
imag=(imag1*real2-imag2*real1)/temp;
cout<<"quotient:"<<real<<"+"<<imag<<"i"<<endl;
}
else
cout<<"divided by 0"<<endl;
}
2009-7-29 -234-
void Modul()
{
double real,imag;
cout<<"input real part and imaginary part:";
cin>>real>>imag;
double modual=sqrt(real*real+imag*imag);
cout<<"modual:"<<modual<<endl;
}
2009-7-29 -235-
void amplitude()
{ double real,imag;
cout<<"input real part and imaginary part:";
cin>>real>>imag;
double amp;
if(fabs(real)>0.000001)
{ amp=atan(imag/real); }
else amp=PI/2;
amp=ANGLE180*AMP/PI;//弧度转换为度显示
cout<<"amplitude:"<<amp<<endl;
}
2009-7-29 -236-
#define指令还可以定义带参数的宏,例如,
#include<iostream>
using namespace std;
#define MIN(a,b) (a<b?a:b)
void main()
{
int x,y;
cout<<”input x,y:”;
cin>>x>>y;
cout<<”minimum:”<<MIN(x,y);
}
2009-7-29 -237-
总结本章详细介绍了关于函数的知识,重点介绍了对函数的引入,定义,原型声明,函数的参数及函数调用,函数是实现算法的基本单位,函数的设计和使用是学习程序设计必须掌握的基本知识 。
函数还有一些特殊的形式,如递归函数,内联函数,具有默认参数值的函数等 。 递归函数便于递归问题的实现,
尤其适合数学上的一些递归问题 。 用递归函数实现算法,
优点是思路简单;缺点是函数执行效率一般不高,因为频繁的递归调用造成的进栈出栈很花时间 。 内联函数等函数的特殊形式,使我们在利用函数实现算法时更方便灵活 。
本章介绍的变量的存储类型以及标识符的作用域等概念,也是我们必须掌握的基本知识。