返回
第 2章 算法设计与程序结构
第 2章 算法设计与程序结构
判断与选择结构
循环结构
常用算法设计
变量的存储属性
多文件程序结构
判断与选择结构
关系运算与逻辑运算
if … else 结构
条件运算符与条件表达式
else if结构
switch结构
关系运算与逻辑运算
关系运算符与关系表达式
逻辑运算符与逻辑表达式
关系运算符
<(小于) <=(小于等于)
>(大于) >=(大于等于)
==等于) !=(不等于)
关系运算符特点
关系运算的结果为逻辑型
“==”与,!=‖两种关系运算
符的优先级比其它关系运算符的
优先级别略低
逻辑运算符
&&(逻辑“与”)
|| (逻辑“或”)
! (逻辑“非”)
if … else 结构
if … else 结构的基本形式为,
if( 判断表达式 )
语句 1
else
语句 2
例程
include <iostream.h>
class ThreeNumber
{
int m1,m2,m3;
public,
void Input3Integers();
void GetMax();
};
int main()
{
ThreeNumber a;
a.Input3Integers();
a.GetMax();
return 0;
}
两个成员函数的实现
void ThreeNumber::Input3Integers()
{
cout << ―Input 3 Integers:\n‖;
cin >> m1 >> m2 >> m3;
}
int ThreeNumber::GetMax()
{
int max;
if (m1 > m2)
if (m1 > m3)
max = m1;
else
max = m3;
else
if (m2 > m3)
max = m2;
else
max = m3;
cout << ―The Max integer is:‖<< max << endl;
}
条件运算符与条件表达式
条件表达式的一般形式为,
e1? e2, e3
else if结构
if(判断表达式 1)
语句 1
else if(表达式 2)
语句 2

else if(表达式 n)
语句 n
else
语句 n+1
switch结构
switch(开关表达式 )
{
case 常量 1,
语句表列
case 常量 2,
语句表列

default,
语句表列
}
switch结构描述例程
void Character::GetClass()
{switch(mCh)
{ case '0',
case '1',
case '2',
case '3',
case '4',
case '5',
case '6',
case '7',
case '8',
case '9',
cout << "\nIt's a digit character.";
break;
case '',
case '\n',
case '\t',
cout << "\nIt's a white character.";
break;
default,
cout << "\nIt's a letter character.";
break;
}
}
循环结构
for结构
while结构与 do … while 结构
for结构
结构的基本形式为,
for( 表达式 1; 表达式 2; 表达式 3 )
循环体
高斯( Gauss)问题
# include <iostream.h>
class Gauss
{ int mi;
public,
InputInteger();
GetGaussSum();
};
int main()
{ Gauss g1;
g1.InputInteger();
g1.GetGaussSum();
return 0;
}
void Gauss::InputInteger()
{
cout << "Enter a character:\n";
cin >> mi;
}
void Gauss::GetGaussSum()
{
int i,isum = 0;
for(i = 1; i <= mi; i ++)
isum += i;
cout << "\nThe sum is:" << isum;
}
for结构的流程
for ( 初始化表达式 ;→判断表达式 ;← 修正表达式 )
↓‖真”
循环体
进入循环
退出循环

while结构
while结构的一般形式,
while( 判断表达式 )
循环体语句
进一步说明
表达式是循环体语句能否进行以及能否
继续的条件。也就是说,在进入循环之
前,先测试一次判断表达式,如果判断表达式为真(值不为 0),则就进入循环,
开始执行循环体语句;以后每执行完一
次循环体语句,就再测试一次判断表达
式,如果判断表达式还为“真”(值不为 0),就继续执行循环体语句;如此重
复,直到某一次循环体语句执行完,判
断表达式变为“假”至
do … while 结构
do … while 结构形式为,
do
循环体语句
while(判断表达式 );
进一步说明
do … while 结构与 while结构非常相
似,一点微小的区别仅在于 while结
构是先判断后执行循环体,而 do …
while结构是先执行一次循环体再判
断。也就是说,do … while 结构最少
要执行循环体一次,而 while结构有
可能一次也不执行循环体
常用算法设计
穷举
递推
模拟
递归
穷举
穷举归纳是数学上最常用的方法之

它的基本思想是一一列举各种可能
进行测试,从中找出符合条件的解
计算机能够实现高速运算,借循环
结构实现穷举,比人工更为有效
大奖赛评分程序
# include <iostream.h>
class Score
{
int mNumber_Adjudicator; // 评委人数
public,
Score(int iNum_Adj)
{
mNumber_Adjudicator = iNum_Adj;
}
void GetResult(); // 计算并给出结果
};
int main()
{
Score m(15); // 假定有 15个评委
m.GetResult();
return 0;
}
void Score::GetResult()
{
int iScore,// 每个评委的给分
i,// 临时变量
max,// 最高分
min; // 最低分
max = 0; // 先假设当前最大分为 0
min = 100; // 先假设当前最小分为最大分( 100)
float fSum = 0; // 均分
for(i = 1; i <= mNumber_Adjudicator; i ++){
cout << "\n输入第, << i << ―个分, ;
cin >> iScore; // 输入评委评分
fSum += iScore; // 计算总分
if(score>max) max = score; // 筛选最高分
if(score<min) min = score; // 筛选最低分
}
cout << "\n均分为 " << (fSum-max-min)/( mNumber_Adjudicator-2));
}
打印 3 ~ N中的素数
# include <iostream.h>
class Primer
{
int mNumber;
public,
Primer(int n)
{
mNumber = n;
}
void GetPrimers();
};
int main()
{
Primer n(m);
n.GetPrimers();
return 0;
}
36块砖,36人搬
class RemoveBricks
{
int iNumberOfPeople;
int iNumberOfBrick;
public,
RemoveBrick(int iNumPeop,int iNumBrc) // 构造函数
{
iNumberOfPeople = iNumPeop ;
iNumberOfBrick = iNumBrc ;
}
void Calculate();
};
int main()
{
BrickProblem rb(36,36); // 用构造函数创建对象并初始化
rb.Calculate(); // 调用对象的成员函数进行计算
return 0;
}
函数 Calculate()
# include <iostream.h>
void BrickProblem::Calculate()
{
cout << "men*****women***children\n");
int men = 0; // 赋初值
while(men ++ < iNumberOfPeople/4) // 修订并判断
{
int women = 0; // 赋初值
while(women ++ < iNumberOfPeople/3) // 修订并判断
{
int children;
children = iNumberOfPeople – men - women;
if(4 * men + 3 * women + children / 2 == iNumberOfBrick)
{ cout.width (6); // 设置输出域宽
cout << men << women << children;
}
}
}
}
递推
递推就是采用不断由已知推出新
值,直到求得解为止
求两个非负整数的最大公因子
# include <iostream.h>
class Gcd
{
int mM,nN;
public,
void GetTwoPositiveInt()
{
cout << "\nEnter 2 positive integers:";
cin >> mM >> mN;
}
Void GetGcd();
};
int main()
{
Gcd gcd1;
Gcd1.GetTwoPositiveInt();
Gcd1.GetGcd();
Return 0;
}
GetGcd()
Void Gcd::GetGcd()
{
int u,v,r;
u = mM;
v = mN;
if(u * v != 0)
{
while(r = u % v)
{
u = v;
v = r;
}
cout << "\nThe gcd is:" << v;
}
else
cout << "\nDivision by zero!";
}
小猴子吃
# include <iostream.h>
class Peach
{
const int mDay = 10;
public,
void GetPeachNum();
};
int main()
{
Peach Peach1;
Peach1.GetPeachNum();
Return 0;
}
GetPeachNum
void Peach::GetPeachNum()
{
int peach = 1,day = mDay;
while(day > 1)
{
peach = (peach + 1) * 2;
day --;
}
cout << "The number of peach is:" << peach;
}
模拟
模拟( simulation)又称仿真,就是利用
模型在实验环境下对真实系统进行研究。
当研究环境是计算机环境时,就是计算机模拟
用计算机进行模拟的一般过程是,
· 描述问题;
· 构造模型;
· 设计模拟算法和程序;
· 运行并验证模型的准确性和精度
时间步长法
导弹追击飞机时,由于飞机在运动,导
弹要不断地调整自己的方向,以保持弹头始终指向飞机
问题是,导弹应以多大的时间间隔进行
方向调整并以多大的速度追击较好。这
一问题看起来简单,它的数学方程的求解却很困难
用计算机进行模拟常常可以获得较满意
的结果
分析
由于导弹与飞机的飞行轨迹都是时间的函数,因此采用时间步长法模拟较为合理。

· dt:时间步长变量;
· va:飞机的速度;
· vm:导弹的速度;
· s:某一瞬间导弹与飞机间的距离。
由图 2.5所示导弹与飞机的位置关系,可以得出,
s=?Ym2 + (Xm - Xa)2
再设导弹飞行的方向角为 ?,则有,
sin ? = (Xm - Ma)/s
cos ? = Ym / s
每经过一个时间步长 dt,导弹与飞机的位置将发生如下变化,
Xm = Xm – Vm * dt * (Xm - Xa)/s
Ym = Ym – Vm * dt * Ym/s
Xa = Xa + Va * dt
这就是按时间步长法得到的导弹追击飞机的模型
class Missile
#include <math.h> // 数学库头文件
class Air_raider;
class Missile
{
float x // 某时刻导弹的坐标
float v; // 导弹速度
public,
Missile ( float x0,float y0,float Vm) // 构造函数
{
x = x0; y = y0; v = Vm;
}
float newXY (Air_raider &a);
};
class Air_raider
class Air_raider
{
float x; // 某时刻飞机的坐标(水
平)
float v; // 飞机的速度
public,
Air_raider( float x0,float va) // 构造函数
{
x = x0; v = va;
}
void newX ( )
{
x = x + DT * v;
}
friend float Missile,,
newXY (Air_raider & ); // 友元函数声明
};
Missile,:newXY (Air_raider &a)
float Missile,:newXY (Air_raider &a)
{
float s;
s = sqrt(x * y + (x - a.x) * (x - a.x));
x = x - v * DT * (x - a.x)/s;
y = y - v * DT * y/s;
return s;
}
程序
#include <iostream.h>
#include <math.h> // 数学库头文件
#define S0 5 // 近似地认为已经追上的最大距离
#define DT 5 // 时间步长
int main()
{
Missile m(4000,100000,10000); // 生成一个导弹对象
Air_raider a(0,500); // 生成一个飞机对象
int steps = 0;
do
{
steps ++; // 计算导弹方向调整步数
a.newX();
} while(m.newXY(a)>S0);
cout << "\n" << steps << ―,‖ << m.newXY(a);
return 0;
}
事件步长法 ----随机抽样
#include <iostream.h>
#include <stdlib.h> // 随机函数 rand()要求的头文件
int main()
{
int m,n;
cout << "\nInput two numbers:";
cin >> m >> n;
for(int i = 1; i <= n; i ++)
cout << (m * rand() / RAND_MAX + 1);
// 产生一个随机数
return 0;
}
递归
自己由自己组成。这种自己由自己直接
组成或自己由自己间接组成的情形,就称为递归
一个函数直接或间接地调用自身,便构
成了函数的递归调用,前者称为直接递
归调用,后者称为间接递归调用
递归调用必须在一定条件下结束,即在
递归描述中,必须写明在什么条件下使
递归结束
n!的递归函数
#include <iostream.h>
#include <stdlib.h>
int rfact(int n)
{
if(n<0)
{
cout << "Negative argument!\n";
exit(-1); // 正常退出
}
else
if(n == 0)
return 1;
else
return n * rfact(n - 1); // 递归调用
}
n=5时 rfact(n)的调用和回代过程








rfact(5) = --------------------------------- = 120
5*rfact(4) = ------------------------ 5 * 24 = 120
4*rfact(3) = ------------------ 4 * 6 = 24
3*rfzct(2) = ------------ 3 * 2 = 6
2*rfact(1) = ------ 2 * 1 = 2
1 = ------------ 1
汉诺塔( Tower of Hanoi)问题
# include <iostream.h>
class Hanoi
{
int mDiscNumber;
public,
Hanoi(int iDiscNum)
{
mDiscNumber = iDiscNum;
}
void MoveDisc(int n,char a,char b,char c);
};
变量的存储属性
变量的作用域与生存期
C++的自动变量与外部变量
静态局部变量
const对象
变量的作用域
C++允许程序员把某一变量定义在某
一域中才可以操作,称为变量的作
用域。通常把作用域分为全局和局
部两种:只能在函数或语句块中起
作用的变量,称为局部变量或内部
变量;可以在一个文件范围内或一
个程序的所有文件范围内起作用的
变量,称为全局变量
变量的生存期
存储在静态存储区中的变量,在编
译时即被创建,在程序运行结束时
才被撤消,相对于程序的生命而言,
其生命是永久的。存储在动态存储
区中的变量,在程序运行过程中被
动态创建,在程序运行完所在域即
被撤消,相对于程序而言,其生命
是动态的
C++的自动变量
自动变量被声明在“内部”(即函数,
块,声明语句之中),如
{
auto a,b,c = 2;
}
这是一种使用最频繁的变量
C++中使用的大量变量都是自动变量,因
此声明符 auto可以缺省
自动变量的性质
自动变量的生存期局部于所在的块
自动变量由于生存期是局部的,其
作用域也局部于其声明语句所出现
的块
一个“内层”变量可以屏蔽同名的
“外层”变量
# include <iostream.h>
int main()
{
void increment();
increment();
increment();
increment();
return 0;
}
void increment()
{
int i;
i++;
cout << i;
}
extern存储类(外部变量)
用 extern声明的变量称为外部变量
外部变量有两种声明形式:定义性
声明与引用性声明。定义性声明是
为了建立实体,引用性声明是为了建立标识符与实体之间的联系。
变量的定义性声明的形式为,
extern 类型 变量名 = 初始化表达
式 ;
引用性声明与定义性声明的区别
定义性声明一定是在外部,而引用性声明不限于在外部,只要在引用之前给出便可
定义性声明具有初始化的作用,而引用性声明
没有
定义性声明有两种形式:带有 extern、初始化
表达式以及定义在外部并缺省 extern。而引用
性声明只有一种形式,带有 extern但不带有初
始化表达式。
定义性声明只能有一次,而引用性声明可以有
多次
# include <iostream.h>
int main()
{
extern int x; // 引用性声明
cout << ―\nx1:" << x;
{
int x = 3; // 屏蔽了全局的 x
cout << "\nx2 = " << x;
}
extern int x; // 引用性声明
cout << "\nx3:" << x;
return 0;
}
int x = 7; // 定义性声明
外部变量特性
外部变量常驻程序的内存固定数据
区,其生存期是静止的,与程序共
存亡
外部变量(变量)是被用编译预分
配方式创建的
外部变量具有全程作用域
外部变量能被局部变量屏蔽不可见
使用了外部变量 i
#include <iostream.h>
int main()
{
void increment(void);
increment();
increment();
increment();
return 0;
}
int i; // 隐含初始化为 0
void increment()
{
i ++;
cout << i;
}
swap()函数交换 #include <iostream.h> int main()
{
extern int a,b; // 引用性声明
a = 5;
b = 3;
void swap();
swap();
cout << ―\na = ― << a << ―,b = " << b );
return 0;
}
void swap()
{
extern int a,b; // 引用性声明
int temp;
temp = a,a = b,b = temp;
}
int a,b; // 定义性声明
输出用“#”组成的 6× 6的方

#include <iostream.h>
int i;
int main()
{
void prt();
for(i = 0; i <= 5; ++i) // 打印 6行
prt();
return 0;
}
void prt()
{
for(i = 0; i <= 5; ++i) // 打印 6个 "#"
cout << "#";
cout << ―\n‖;
}
外部变量被局部变量屏蔽不可见
# include <iostream.h>
int x = 5; // 声明一个全局变量 x
int main()
{
cout << "\nx1 =" << x; // 全局 x 可见
int x = 3;
cout << ―\nx2 =" << x; // 全局 x不可见
return 0;
}
# include <iostream.h>
int x = 5;
int main()
{
cout << "\nx1 =" << x; // 全局变量 x可见
int x = 3; // 声明局部变量 x
cout << "\nx2 =" << x; // 局部变量 x可见
cout << "\nx3 =" <<,:x; // 全局变量 x可分辩
return 0;
}
静态局部变量
静态变量的生存期与外部变量相
同,也是永久的。但是,静态变
量的作用域可以是全程的,也可
以是局部的。这一小节先讨论局
部静态变量
例 2.4.10
#include <iostream.h>
int main()
{
int i;
static int a;
int b;
i = 1; a = 10; b = 5;
cout << ―*******main*******\n";
cout << ―i = ― << i << ―,a = ― << a << ―,b = ‖ << b << endl;
void sub();
sub() ;
cout << ―*******main*******\n";
cout << ―i = ― << i << ―,a = ― << a << ―,b = ‖ << b << endl;
return 0;
}
void sub()
{
int i;
static int a;
i = 18; a = 200;
cout <<,*******sub*******\n";
cout <<,i =, << i <<,,a =, << a << endl;
}
对静态成员的使用再作几点说明
在类定义之外定义静态成员函数时,不使用static。
静态成员函数也可以定义成内嵌的。
类中的任何成员函数都可以访问类对象的静态
成员,如例 2.5.5中的构造函数与释放函数都访
问了静态数据成员。
静态成员函数由于不与特定的对象相联系,调
用时使用方法
类名,,静态成员函数名 ()
较好
const对象
对象是一种特殊的变量。与数据对象一样,类对象也可以被声明为 const对象。
一般情况下,能用于 const对象的方法只
有构造函数与释放函数,因为 const对象
被看作只能生成与撤销,不能访问的对象
const对类的成员函数具有特定的语义。
一个 const对象,只允许被声明为 const的
方法函数引用
多文件程序结构
多文件程序结构与程序项目
文件包含与条件编译
多文件程序中变量的连接属性
多文件程序结构
一个程序规模很小时,可以把源代
码编写在一个源文件中。而一个程
序的规模较大时,为了进行编写和
编译、调试的便利,常常要把它分
成多个源文件。每一个源文件就成
为该程序的一个编译单元 ——编译
器总是以文件为单位工作的
程序源代码的分割时要注意
在一个程序的多个源文件中,只能有一
个文件具有主函数 main()
每一个函数都应当完整地存放在一个文
件中
一个类内联成员函数,不可与其类定义
(类接口)分开
为了便于链接,要将同一个源程序的多
个源文件定义成一个项目( project)文件
程序项目
从软件工程的角度,每个程序的开发工作,都是一项
工程项目( Project):它要涉及计算机以及相关专业
等领域的知识及其应用,还要使用代码生成、编辑、
编译、链接、调试等一系列的工具
同时,所生成的程序,不只是一个文件,而需要一组
相互关联的源文件和一些资源文件的支持
在集成化的开发环境中,把这一组相互关联的源文件、
资源文件以及支撑这文件的类的集合称为一个工程项
目( Project,后面简称做项目)
项目
项目是应用程序开发的基本单位,用户的工作都是围绕项目进行的
开发一个应用程序时,首先要确定一个项目名,
这个项目名也就是最后生成的应用程序(可执
行文件)名
项目经编译、连接,生成可执行文件后,便可
认为该任务的最终完成。 Visual C++支持项目
管理,并以项目工作区的形式来组织文件、项
目和项目配置
在项目工作区中,给出了构成项目的源文件、
资源文件以及支撑这些文件的类
项目管理
项目管理把组成一个项目的多个
文件作为一个整体来处理。这个
整体称为项目文件。在组装项目
文件之前先要给项目文件命名。
项目文件名是一个C ++标识符
注意
项目文件含有环境信息。当环境
(如盘号、路径、设置的参数等)
改变时,应重新建立项目文件,否
则可能出现莫明奇妙的错误
一个项目文件要涉及许多头文件,因此要注意它们之间的一致性
一个项目文件必须且只能有一个主函数
文件包含
使用 #include指令进行文件包含,其实质
就是在预处理时将一个源文件嵌入到当
前文件中的指令所在处。
文件包含命令有两种格式
一种格式是巳经熟悉的格式,
#include <文件名 >
另一种格式是,
#include "文件名 "
好的头文件包含如下一些内容
类型定义,如定义一个枚举类型 enum color
{RED,BLUE,GREEN,YELLOW};
函数声明,如 extern int strlen {const char*};
嵌入函数声明,如 inline char get() {return *p++};
数据声明,如 extern int a;
常量定义,如 const float pi=3.141593;
包含指令,如 #include <iostream.h>
宏定义,如 #define case break; case
注释
好的头文件不能包含以下内容
一般函数定义
数据定义,如 int a
常量聚集定义,如 const
tbl[]={/* … */}
条件编译
条件编译是源文件内系统级的选择结构,其格式有,
#if 常量表达式 1
语句或预处理指令表列 1
#elif 常量表达式 2
语句或预处理指令表列 2

#elif 常量表达式 n
语句或预处理指令表列 n
#else
语句或预处理指令表列 n+1
#endif

#ifdef标识符
语句或预处理指令表列 1
#else
语句或预处理指令表列 2
#endif

#ifndef标识符
语句或预处理指令表列 1
#else
语句或预处理指令表列 2
#endif
程序的跟踪调试
在程序设计时为了便于检查程序
是否有预想的中间结果或想了解
程序执行的过程时,可以增加一
些输出语句
使用条件编译可以使这些语句仅
在调试过程中被编译
例 2.5.3
#include <stdio.h>
#define DEBUG
int factorial(int num)
{
if(num==0)
return 1;
else
{
#ifdf DEBUG // 条件编译开始
int i;
cout << "call times:‖ << ++i << ―num=" << num <<endl;
#endif // 条件编译结束
return (factorial(num-1)*num);
}
}
多文件程序中变量的连接属性
在多文件程序结构中,如何在不
同的源文件中使用同一个变量和
如何在不同的源文件中使用一个
变量而不管别的源文件中是否也
在使用这个名字,为名字的连接
属性。前者用外部变量实现,后
者用静态变量实现
外部变量的连接属性
当一个程序的不同源文件中要使
用某一个公共变量时,应当使用
外部变量
// 建立项目文件 ex2_5_3.prj
ex2_5_3.cpp
ex2_5_3_1.cpp
//定义文件 ex2_5_3.cpp
#include <iostream.h>
void swap(); // 函数声明
extern int a,b; // 引用性声明,a,b定义在别的文件中
int main()
{
a = 5;
b = 3;
swap();
cout << ―\na = ― << a << ―,b = " << b );
return 0;
}
//定义文件 ex2_5_3_1.cpp
int a,b; // 定义两个外部变量
void swap()
{
int temp;
temp = a,a = b,b = temp;
}
全程静态变量
全程静态变量与外部变量的区别在
于,外部变量的作用域全局于组成
程序的所有文件,而全程静态变量
的作用域仅全局于其所在的文件。
使用局部静态变量的好处是各编译
单元分别设计时,不需要考虑别的
编译单元中是否使用了相同的全局
变量名