热身问答
- Flow Chart的中文意思是什么?
- 流程图
- 请说出自然界中河流的三种流动方式。
顺流、逆流- 向着一个方向流淌;流着流着产生支流;卷成漩涡。
- 与河流的流动方式一样,程序的流程也分为三种。在程序中,把犹如水流向着一个方向流淌的流程称作
“顺序执行”;把犹如水流流着流着产生了支流的流程称作“条件分支”;把犹如水流卷成漩涡的流程称作“循环”。
- 事件驱动是什么?
- 用户的操作等产生事件后,由事件决定程序的流程
-
Windows 应用程序的运行就是由事件驱动的。例如,选择“打开文件”菜单项就能打开一个窗口,在里面可以指定要打开文件的名称和存储位置。之所以能够这样是因为一旦触发了“选中了菜单项”这个事件,程序的流程就相应地流转到了处理打开窗口的那部分。
4.1 程序的流程分为三种
想象计算机的运作方式:
计算机的硬件系统由 CPU、I/O 和内存三部分构成。内存中存储着程序,也就是指令和数据。CPU 配合着由时钟发生器发出的滴答滴答的时钟信号,从内存中读出指令,然后再依次对其进行解释和执行。
- 顺序执行: 按照指令记录在内存中的先后顺序依次执行的一种流程
- 程序从内存中的低地址开始向着高地址流下去
- 首先CPU中有一个称为PC(程序计数器)的寄存器, 存储了CPU要执行的下一条指令的地址。 也就是, 每解释执行完一条指令, PC寄存器的值就会自动被更新为下一条指令的地址。
- 而大多数情况下, PC寄存器的值只会增加。 假设 PC 寄存器正指向内存中一个从 10 号地址开始的 3 字节指令。CPU 解释执行完这条指令后,PC 寄存器中的值就变成 10+3 = 13 了。
- 这就是从低地址(编号较小的地址)向着高地址(编号较大的地址)逐个执行指令。
- 条件分支: 根据若干个条件的成立与否, 在程序的流程中产生若干分支的一种流程。
- 循环: 在特定范围内反复执行若干次的一种流程
代码清单 4.1 中列出了用 VBScript(Visual Basic Scripting Edition)编写的“石头剪刀布游戏”的代码,VBScript 是 BASIC 语言的一个版本。该程序可以在 Windows 98/Me/2000/XP 操作系统上运行A。玩家和计算机可以进行五轮石头剪刀布比赛,比完后会显示玩家获胜的次数。
可以用记事本等文本编辑器编写这个程序,并存储到扩展名为“.vbs”的文件中,比如 ShitouJiandaoBu.vbs。然后双击文件即可运行。
代码清单 4.1 用 VBScript 编写的“石头剪刀布游戏”
' 初始化表示手势的变量
Dim gesture(2)
gesture(0) = " 石头 "
gesture(1) = " 剪刀 "
gesture(2) = " 布 "
' 初始化对玩家获胜次数计数的变量
wins = 0
' 初始化随机数种子
Randomize
' 显示程序启动信息
MsgBox " 石头剪刀布游戏 Ver.1.00 by H.Yazawa"
' 进行五轮比试
For i = 1 To 5
' 输入玩家的手势
user = CInt(InputBox("0: 石头、1: 剪刀、2: 布 "))
' 用随机数决定计算机的手势
computer = CInt(Rnd * 2)
' 生成提示双方出的手势的字符串
s = " 玩家:" & gesture(user) & "、计算机:" & gesture(computer)
' 判定胜负,显示结果
If user = computer Then
MsgBox s & "... 平局! "
ElseIf computer = (user + 1) Mod 3 Then
MsgBox s & "... 玩家获胜! "
wins = wins + 1
Else
MsgBox s & "... 计算机获胜! "
End If
Next
' 显示玩家的获胜次数
MsgBox " 玩家获胜次数: " & wins
' 初始化表示手势的变量
Dim gesture(2)
gesture(0) = "石头"
gesture(1) = "剪刀"
gesture(2) = "布"
' 初始化对玩家获胜次数计数的变量
wins = 0
' 初始化随机数种子
Randomize
' 显示程序启动信息 (顺序执行下面的程序)
MsgBox "石头剪刀布游戏 Ver.1.00 by Monica"
' 进行五轮比试 (循环)
For i = 1 To 5
' 输入玩家的手势
user = CInt(InputBox("0: 石头、 1:剪刀、2:布"))
' 用随机数决定计算计算机的手势
computer = CInt(Rnd * 2)
' 生成提示双方出的手势的字符串
s = "玩家: " & gesture(user) & "、 计算机: " & gesture(computer)
' 判定胜负, 显示结果 (条件分治)
If user = computer Then
MsgBox s & "...平局!"
ElseIf computer = (user + 1) Mod 3 Then
MsgBox s & "...玩家获胜!"
wins = wins + 1
Else
MsgBox s & "...计算机获胜!"
End If
Next
' 显示玩家的获胜次数
MsgBox "玩家获胜次数: " & wins
错误:Microsoft VBScript 编译器错误 未结束的字符串常量(800A0409)
解决方案:将文件编码从UTF-8改为ANSI格式。
原因: 可能和中文有关系。 ANSI在中文操作系统默认编码是GB2312,GB2312编码适用于汉字处理
4.2 用流程图表示程序的流程
Flow Chart可以帮助我们编写程序, 在准备编写之前用来思考程序的流程。
大部分情况下我我们只需要使用下图中的最基础中的符号就可以完成我们的程序构思:
4.3 表示循环程序块的”帽子“和”短裤“
作者将上面的一堆符号称作“帽子和短裤”
帽子和短裤表示循环, 中间就是需要循环的步骤。 如果循环中嵌套循环, 那么需要有两对有各自名字的帽子和短裤。
如何实现循环?
机器语言或汇编语言下:
- 循环是通过当满足条件时就返回到之前处理过的步骤来实现的。
- 所以我们使用”跳转指令“来将PC寄存器的值设为之前执行过的步骤所对应的内存地址, 就构成了循环。
高级语言下:
在前面的 VBScript 中,是用 For 和 Next 两个关键字表示循环的程序块的。For 对应着“帽子”,Next 则对应着“短裤”。For 的后面写有循环条件。“For i = 1 To 5”表示用变量 i 存储循环次数,将 i 的值从 1 加到 5,每进行 1 次循环就增加 1,如果 i 的值超过了 5 循环就终止。画图时循环条件也要写在“帽子”中
条件分支:
- 通过条件分支返回到之前处理过的指令来实现循环。 跳转到之前处理过的步骤就是循环;跳转到之后尚未处理的步骤就是条件分支
- 在 VBScript 中,使用 If、ElseIf、Else、End If 表示条件分支的程序块。 通过这几个关键字形成了一个被分成三个区域的程序块。
4.4 结构化程序设计
结构化程序设计
- 是由学者戴克斯特拉(Dijkstra)提倡的一种编程风格
- 为了把程序编写得具备结构性, 仅使用顺序执行、条件分支和循环表示程序的流程即可, 而不再使用跳转指令。
- 不再使用跳转指令的含义:不使用跳转指令但是可以用顺序执行、条件分支和循环表示程序的完整流程。
- 作为计算机硬件上的行为,无论是条件分支还是循环都必须使用跳转指令实现。但是在高级语言中,可以用 If~ElseIf~ Else~End If 程序块表示条件分支,用 For~Next 程序块表示循环。跳转指令因此就变得可有可无了。但是即便如此,在很多高级语言中,还是提供了与机器语言中跳转指令相当的语句,例如 VBScript 中的GoTo 语句。
- 为什么不使用: 因为跳转指令会使程序陷入到流程错综复杂的状态
- 不再使用跳转指令的含义:不使用跳转指令但是可以用顺序执行、条件分支和循环表示程序的完整流程。
4.5 画流程图来思考算法
算法Algorithm: 就是解决既定问题的步骤。
那么我们可以通过画流程图来解决问题然后将其转换成程序的流程。
思考算法时, 我们需要先从整体上考虑程序的粗略流程, 然后再考虑各个部分细节的流程。 (细节将在下一章介绍。 )
几乎所有的程序从整体来看都是“初始化处理”→“循环处理”→“收尾处理”这么一个一成不变的流程。
- 我们从用户的角度来思考程序。首先,用户启动了程序(程序执行初始化处理)。接下来用户根据自己的需求操作程序(程序进入循环处理阶段)。最后用户关闭了程序(程序执行收尾处理)。这样的使用方法就可以直接作为程序的整体流程。
4.6 特殊的程序流程
两种特殊的程序流程——中断处理和事件驱动(Event Driven)
中断处理
- 指计算机使程序的流程突然跳转到程序中的特定地方,这样的敌方被称为中断处理例程(Routine) 或是中断处理程序(Handler),而这种跳转是通过 CPU 所具备的硬件功能实现的。
- 也就是由于外部的原因使正常的流程中断,中断后再返回到之前流程的过程就是中断处理流程
在第二章我们讨论过, Z80 CPU中有两个引脚INT和NMI, 它们可以接收从 I/O 设备发出的中断请求信号A。以硬件形式连接到 CPU 上的 I/O 模块会发出中断请求信号,CPU根据该信号执行相应的中断处理程序。在诸位使用的个人计算机上,中断请求信号是由连接到周边设备上的 I/O 模块发出的。
- 例如每当用户按下键盘上的按键,键盘上的 I/O 模块就会把中断请求信号发送给CPU。CPU 通过这种方式就可以知道有按键被按下,于是就会从 I/O设备读入数据(如图 4.14 所示)
- I/O的中断请求 ——> INT/NMI接受请求然后连接到CPU的I/O模块 再发出中断请求信号——> CPU根据信号执行对应的中断处理程序。
INT: Interrupt, 中断。 用于从Z80 PIO向Z80 CPU发出中断请求的引脚。
- 中断就是让 CPU 根据外部输入的数据执行特定的程序。
INT 引脚用于处理一般的中断请求。NMI 引脚则用于即使 CPU 屏蔽了中断,也可在执行中的指令结束后立刻响应中断请求的情况。
4.7 特殊的程序流程 —— 事件驱动
事件驱动:
-
事件驱动是一种适用于 GUI 环境的编程风格,在这种环境中用户可以通过鼠标和键盘来操作应用程序。
-
将事件驱动想象为两个程序在对话。例如:
-
下图中有WinMain()和 WndProc()两个函数(代码块)。 WinMain() 是在程序启动时被调用的主例程(Main Routine)。而 WndProc()只会被Windows操作系统调用。 于是这就使得 这段程序可以和Windows进行对话。
-
-
事件Event: 指用户在应用程序中点击鼠标或者敲击键盘这样的操作。
-
事件驱动:责检测事件的是 Windows。Windows 通过调用应用程序的 WndProc() 函数通知应用程序事件的发生。而应用程序则根据事件的类型做出相应的处理。这种机制就是事件驱动。
- 可以说事件驱动也是一种特殊的条件分支,它以从 Windows 送来的通知为条件,根据通知的内容决定程序下一步的流程。
- 要实现事件驱动,就必须把应用程序中的 WndProc() 函数(称为窗口过程,Window Procedure)的起始内存地址告诉 Windows。这一步将在应用程序 WinMain() 中作为初始化处理被执行。
虽然事件驱动的流程也可以用流程图表示,但是由于要排列很多的菱形符号(表示条件分支),会很复杂, 所以我们使用”状态转化图“来表示事件驱动。状态转化图中有多个状态,反映了由于某种原因从某个状态转化到另一个状态的流程。
状态转化表:
总结
程序的流程只有顺序执行、条件分支和循环三种。
- 顺序执行通过CPU中的PC寄存器的值的自动更新实现。因为PC寄存器存储了CPU将要执行的下一条指令的地址。
- 条件分支和循环在机器语言和汇编语言中通过跳转指令表示, 在高级语言中用程序块表示, 在硬件上是通过把 PC 寄存器的值设为要跳转到的目的地的内存地址来实现。