版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第5章 基本结构程序设计,5.1 程序设计的一般过程 5.2 顺序结构程序设计 5.3 分支程序设计 5.4 循环程序设计 5.5 字符处理,5.1 程序设计的一般过程,5.1.1 程序与程序设计的概念 要用计算机解决问题,必须事先对所要解决的问题拟定一个便于计算机工作的明确步骤,并且用计算机所能理解的“语言”把它表示出来,输入计算机,经过调试正确,并最后运行取得结果后才算完成了任务,我们称这一过程为程序设计。把用计算机语言表示的问题求解的一系列明确步骤,称作解决该问题的程序,而把完成这项任务的活动称为编码(或称编制程序,简称编程);拟定一个便于计算机工作的明确步骤称为算法设计(或称设计问题求
2、解的方法)。程序设计与编制程序是有区别的。程序设计除了包括编码外,还有其他过程,如算法设计,程序测试等。,5.1.2 算法与流程图 1算法 在编制程序之前,必须设计算法。算法是求解问题的方法和具体步骤,即把所要解决的问题表达为一系列计算机所能执行的基本操作。 例如:任意给定一个正整数N,求出一切不超过N的素数。 如果用筛选法来解决这一问题,则求解的步骤可概述如下:,(1) 将不超过N的整数从小到大排成一串,即 1,2,3,4,N-1,N (2) 划去数串最左边的数1(可使用*号标在该数的右上角),即 1*,2,3,4,N-1,N 此时数串留下的最左边的数为2,而2是一个素数,它是已识别的当前素
3、数。,(3) 从当前素数2起,划去数串右边所有2的倍数的数(2本身除外),即 1*,2,3,4*,5,6*,7,8*,9,10*, 这样划去的都是合数,而右边剩下的就可能是素数。从2起,往数串右边看,首先遇到的是素数3。 (4) 再从当前素数3起,划去数串右边所有3的倍数的数(3本身除外),即 1*,2,3,4*,5,6*,7,8*,9*,10*,11, 这样划去的都是合数。再从3起往右边看,首先遇到的就是素数5。,如此继续下去,一直到找出的数(此数不包括在所求素数中)刚好超过N为止。这时,从该数起,数串左部中没有被划去的所有数就是所求的不超过N的全部素数。 上述这样一个工作步骤,就是求出一切
4、不超过N的素数的“算法”。,2流程图 在上述例题中,对于求解问题的算法描述用的是自然语言,包括一些数学语言。但用这种方法来描述算法,对于求解问题的流程(即逻辑结构)还不是很直观,人们理解起来也比较慢。因此,人们往往利用具有几何图形直观性的流程图(简称框图)方法来描述算法,即给出问题求解步骤的图形表示,或是用某种类高级语言(如类PASCAL语言)来描述算法,而流程图是描述算法最早使用的一种方法。 流程图是框和线(或带箭头的线)的集合体。框用以指示做什么事,线用以指示各框之间的关系(包括顺序关系)。框具有各种形状,表示各种不同的用途。国际标准化组织ANSI/ISO对流程图使用的各种图形符号及其含义
5、作了规定,本书不再讨论。,5.1.3 程序设计语言与编码 用计算机来求解问题,还必须用计算机所能接受的语言将问题的算法表示出来,即编码或编程序。 编程序首先碰到的是使用何种语言的问题,这取决于两个因素,一是求解问题或任务的性质和要求,二是所用的计算机配置了什么语言。,如前所述,在计算机系统的最内层是机器语言,它是裸机能直接理解的惟一语言。用机器语言编写程序虽可充分利用机器指令的灵活性,达到较高的效率,但这种程序是二进制代码形式,难读难写不便修改,现在已很少使用它,改用其外层的汇编语言来写“要求效率较高”的程序。汇编语言的主要思想是用符号表示机器指令,即用“记忆码”代替操作码,用“标识符”代替地
6、址。与机器语言相比,它易读、易写,可以减轻人们的劳动。然而机器并不能直接理解汇编语言,因此必须要有一个翻译程序将汇编语言程序翻译成机器语言程序,这就是汇编程序。汇编语言是一种重要的程序设计语言。,除了汇编语言,还有许多高级程序设计语言,如PASCAL、Visual C+等。使用这类高级语言编写的程序,同汇编语言程序一样,也必须经过相应语言的“编译程序”的翻译,使之成为机器语言程序,才能在机器上执行。,5.1.4 程序设计的一般过程 程序设计包含了多方面的工作,尤其是解决大型复杂问题更是如此。程序设计一般要经历以下几个阶段。 1.定义问题 这个阶段也称为要求定义分析,或称需求分析,即对要解决问题
7、的意义和要求,了解明白准确。这包括制定一系列清晰而无二义性的规格说明。例如,问题要求什么样的精度,提供的输入是什么,以及期望的结果又是什么等等。这个阶段是十分重要的,对复杂问题更是这样。因为,需求分析产生的规格说明书是以后各阶段的依据,如果不把问题的含义弄明白、准确,那将会导致整个设计的失败或返工。,2构造解法概要 这个阶段也称为功能设计,主要是制定整个解法的概要,即总体设计。这包括将整个问题分解成若干任务或子任务(可按功能划分任务),以及它们之间相互关系的描述。可采用功能模块分割法、逐步求精法等。,3确定算法 这个阶段也称为设计,主要是选择最优算法和数据结构以实现上述的每一个任务(或子任务)
8、,即根据功能模块而选择最适当的方法和数据结构以实现之。这是程序设计的核心步骤,也是比较困难的工作。,4编码 这个阶段也称为程序编制或编程,主要是选择一种程序设计语言,并用此语言具体地实现所设计的算法。 5调试与排错 通过调试来排除程序中的错误,保证程序的正确。一般要作静态检查和动态运行。静态检查包括人工检查和上机进行语法检查;动态运行就是根据程序在工作中所有可能的情况,例如,输入各种不同的初始数据,检查程序执行是否正确。,6整理文档 程序设计的结果包括两大部分,一是程序,二是文档。这里讲的文档主要是包括各个设计阶段的规格说明书,以及用户使用手册等。这是用户使用、维护程序的依据。 7软件测试 测
9、试是保证软件质量的重要手段,其主要方式是在设计测试用例的基础上检验软件的各个组成部分。首先是进行单元测试,查找各模块在功能和结构上存在的问题并加以纠正。其次是进行组装测试,将已测试过的模块按一定顺序组装起来。最后按规定的各项需求,逐项进行有效性测试,决定已开发的软件是否合格,能否交付用户使用。,8运行/维护 已交付的软件投入正式使用,便进入运行阶段,这一阶段可能持续若干年甚至几十年。软件在运行中可能由于多方面的原因,需要对它进行修改。其原因可能有:运行中发现了软件中的错误需要修正;为了适应变化了的软件工作环境,需做适当变更;为了增强软件的功能需做变更等。,5.2 顺序结构程序设计,例5-1 若
10、内存的数据段中,有缓冲区BUFFER,存取数据的规则是先存储一个16位带符号的被除数,再存储一个16位带符号的除数,接着存储商,最后存储余数。 DATA SEGMENT BUFFER DW 812DH ;被除数 DW 013CH ;除数 DW ? ;存商 DW ? ;存余数 DATA ENDS,STACK SEGMENT PARA STACK STACK DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START PROCFAR PUSH DS MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX
11、,LEA BX,BUFFER MOV AX,BX CWD ;扩展为32位 IDIV 2BX ;带符号数除法 MOV 4BX,AX ;存商 MOV 6BX,DX ;存余数 RET START ENDP CODE ENDS END START,例5-2 编制一个实现两个字(16位)相乘的程序。 MY_DATA SEGMENT M1 DW 00FFH ;被乘数 M2 DW 00FFH ;乘数 P1 DW ? ;存积 P2 DW ? MY_DATAENDS STACK SEGMENT PARA STACK STACK STAPN DB 100 DUP(?) STACK ENDS COSEG SEGME
12、NT,ASSUME CSCOSEG,DSMY_DATA,ESMY_DATA STR PROC FAR MULT: PUSH DS MOV AX,0 PUSH AX MOV AX,MY_DATA MOV DS,AX MOV ES,AX MOV AX,M1,MUL P2 MOV P1,AX ;存结果 MOV P2,DX RET STR ENDP COSEG ENDS END MULT,5.3 分支程序设计,一般情况下,程序按顺序方式执行,有时也需要机器能根据不同情况,执行不同的程序或程序段,这就要求所编制的程序具有判断、选择的能力,即需要分支结构的程序。计算机的分析、判断能力就是这样实现的。 分支
13、结构的基本形式如图5-1所示,当分支条件满足时执行程序段1,不满足时执行程序段2,当程序段2为空时,分支结构为简单形式。,图5-1 分支结构示意图,5.3.1 转移指令 转移指令可以改变程序的执行顺序,它分为无条件转移指令和条件转移指令。 1无条件转移指令 格式: JMP OPRD 功能:无条件转移到OPRD所指定的位置。 说明:根据目标地址OPRD的位置,本指令还可继续划分为:段内直接短转移、段内直接转移、段内间接转移、段间直接转移和段间间接转移五种具体的形式。,(1) 段内直接短转移。 格式: JMP SHORT LABEL 说明:LABEL为转移后目的地址的标号,转移后的地址标号必须在-
14、128+127之间。 (2) 段内直接转移。 格式: JMP NEAR LABEL 说明:LABEL为转移后目的地址的标号,转移后的地址标号必须在-32 768 +32 767之间。此外,一般将段内直接短转移和段内直接转移合并起来,写为JMP LABEL的形式,具体属于哪种转移由CPU在执行期间自动判断。,(3) 段内间接转移。 格式: JMP reg/mem 说明:转移的偏移量在寄存器/存储器中。 (4) 段间直接转移。 格式: JMP FAR PTR LABEL 说明:LABEL为转移后目的地址的标号,转移后的地址与指令所在的地址不在同一个代码段中,因此CS和IP均需改变。,(5) 段间间
15、接转移。 格式: JMP DWORD PTR mem 说明:转移后目的地址在存储器中,因为CS和IP均需改变,所以需要32位存储器操作数。,例5-3 JMP END ;转移到标号END处,CS不变,IP改变 JMP FAR PTR NEXT ;转移到标号NEXT处,CS改变,IP改变 JMP DWORD PTR SI ;将DSSI开始的32位数据作为转移地址, ;CS改变,IP改变,2条件转移指令 80 x86的条件转移指令比较多,它以某些标志位的逻辑运算作为依据,若满足指令所规定的条件,则程序执行转移,否则顺序执行。在这类指令中,转向的目的地址必须在转移指令的-128列或+127个字节之间,
16、这些指令对标志位无影响,指令的助记符也不惟一。 格式: JCC LABEL 功能: 如果满足判断条件CC则转移到标号LABEL处,否则继续执行下一条指令。,说明:本指令还可继续划分成以下四种情况: (1) 检测某一标志位的条件转移指令如表5-1所示。,表5-1 检测某一标志位的条件转移指令,(2) 根据两个无符号数比较结果来决定转移的条件转移指令如表5-2所示。,表5-2 根据两个无符号数比较结果来决定转移的条件转移指令,(3) 根据两个带符号数比较结果来决定转移的条件转移指令如表5-3所示。,表5-3 根据两个带符号数比较结果来决定转移的条件转移指令,(4) CX/ECX为0时转移的条件转移
17、指令如表5-4所示。,表5-4 CX/ECX为0时转移的条件转移指令,例5-4 设(AL)=(DL),执行指令 CMP AL,DL JZ NEXT 的结果是:指令JZ NEXT执行完后将跳转到NEXT标号处继续执行。 例5-5 设CL内容为一无符号数,执行指令 CMP CL,10 JB NEXT 的结果是:如果(CL) 10,则跳转到NEXT标号处继续执行;如果(CL)10,则继续执行下一条指令。,例5-6 设CL内容为一带符号数,执行指令 CMP CL,0 JNG NEXT 的结果是:如果CL为负数或0,则跳转到NEXT标号处继续执行;如果CL为正数,则继续执行下一条指令。,5.3.2 分支
18、程序设计 在分支程序中,对条件的判断结果只有两种:“是”或者“不是”。因此,分支程序一般有两种结构:不完全的双分支结构和完全的双分支结构,如图5-2所示。图5-2(a)是不完全的双分支结构,图5-2(b)是完全的双分支结构。在不完全的双分支结构中,一个分支需要完成一定的任务,而另一个分支不需要完成任何任务;在完全的双分支结构中,两个分支都分别有各自的任务。,图5-2 分支程序的一般结构 (a) 不完全的双分支结构;(b) 完全的双分支结构,例5-7 给定两个数X和Y,分别在X、Y是无符号数和带符号数的情况下比较X、Y的大小。若X大,则输出字母X,若Y大,则输出字母Y。 NAME EX_COMP
19、ARE_XY STACK STACKSEG 500 MYDATA SEGMENT RW X DW 0F040H ;指定X、Y的值 Y DW 7321H MYDATA ENDS MYCODE SEGMENT ER ASSUME DSMYDATA,SSSTACK,CSMYCODE,START:PUSH DS MOV AX,0 PUSH AX MOV AX,MYDATA MOV DS,AX MOV AX,X CMP AX,Y ;无符号数情况下的比较 JA XN_BIG MOV DL,Y ;Y大,输出Y MOV AH,02H INT 21H,XN_BIG:MOV DL,X ;X大,输出X MOV AH
20、,02H INT 21H MOV AX,X CMP AX,Y ;带符号数情况下的比较 JG XS_BIG MOV DL,Y ;Y大,输出Y MOV AH,02H INT 21H,XS_BIG:MOV DL,X ;X大,输出X MOV AH,02H INT 21H MOV AH,4CH INT 21H MYCODE ENDS END START,1利用比较转移指令实现分支 用于比较、判断的指令有:比较指令CMP、串比较指令CMPS、串搜索指令SCAS。用于实现转移的指令有:无条件转移指令JMP和各种类型的条件转移指令。它们都可以互相配合以实现不同情况的分支。对于多路分支的情况,可以采用多次判断转
21、移的方法实现。每次判断转移形成两路分支,n次判断转移可以形成n+1路分支。,例5-8 数据块的传送。 把内存中某一区域的源数据块传递到另一区域。若源数据块与目的数据块之间地址没有重叠,可直接用数据传送指令或串操作中的传送指令实现。若它们之间的地址重叠,如何实现数据块的传送呢?试编制一程序来解决这种情况的数据块传送问题。 问题分析:先判断源地址加数据块长度是否小于目的地址,若小于,可按增量的方式传送;若不小于,则要把指针改为指向数据块的底部,然后采用减量方式传送。(这里假设从低地址区域向高地址区域传送。)程序描述如下:,DATA SEGMENT STRG DB 1000 DUP(?) STG1
22、EQU STRG+7 STG2 EQU STRG+25 STRSE EQU 50 DATA ENDS STACK SEGMENT PARA STACK STACK STAPN DB 100 DUP(?) STACK ENDS COSEG SEGMENT ASSUME CSCOSEG,DSDATA, ESDATA,SSSTACK,GOO PROC FAR PUSH DS MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX MOV ES,AX MOV CX,STRSE ;计数器值送CX MOV SI,OFFSET STG1 MOV DI,OFFSET STG2 CLD ;
23、增量方式(DF标志清零),PUSH SI ;将偏移量STG1入栈 ADD SI,STRSE-1 ;源地址加上块长 CMP SI,DI ;比较源串地址与目的串地址的大小 POP SI ;将偏移量STG1出栈 JL OK ;若SIDI转移,表明无重叠区,按增量方式传送 STD ;减量方式传送,表明有重叠区 ADD SI,STRSE-1 ;指向数据块底部 ADD DI,STRSE-1 OK: REP MOVSB ;重复传送50个数据 RET GOO ENDP COSEG ENDS END GOO,本程序设计时,考虑了两种情况:一是无重叠数据源,即源数据块长度小于目的地址,按增量方式传送;二是有重叠数
24、据源,即源数据块长度大于目的地址,按减量方式传送。任何一个串操作指令,可在其前面加上一个重复操作前缀REP,于是指令就重复执行直至在寄存器CX中的操作次数满足要求为止。重复操作是否完成的检测是在操作以前进行的:若初始化操作次数为0,它不会引起重复操作;若基本操作是一个影响ZF标志的操作,在重复操作前缀字节中也可以规定与标志ZF相比较的值。在基本操作执行以后,ZF标志与指令的值不等,则重复终结。,在重复的基本操作执行期间,操作数指针(SI与DI)和操作数寄存器在每一次重复后修改,然而指针将保留重复前缀字节的偏移地址。若一个无重复操作指令,被外部中断源中断,则在中断返回以后,可以恢复重复操作指令。
25、应避免串操作指令的重复前缀与别的两种前缀同时使用。CLD指令是DF标志清零指令,即使DF=0,将串操作指令设置为自动增量指令。,2利用表实现分支 在处理实际问题时,对于分支比较少的情况用IF_THEN_ELSE结构是最简便可行的,但对于分支很多的情况,再使用IF_THEN_ELSE结构实现程序就会变得十分冗长。例如,编制一个具有建立文件、修改文件、删除文件、显示文件和退出应用程序返回操作系统功能的管理文件的菜单程序,最好不要用IF_THEN_ELSE结构。在这种情况下,比较好的方法是使用CASE结构来实现分支。在汇编语言中,通常是使用跳转表法实现CASE结构。,具体实现时,需要两个步骤:一是构
26、造成跳转表,二是使用跳转地址、转移指令或关键字实现分支。跳转表存储时可采用连续存储的方法,可将跳转表存储在内存的一片连续单元中,表中的内容可以是跳转地址、转移指令或关键字等。根据跳转表中存储的内容,将利用表实现分支,即根据表内地址分支,根据表内转移指令分支或根据表内关键字分支这样三种方法,如图5-3所示。,图5-3 利用表实现分支及其分类,1) 根据表内地址分支 例5-9 某工厂有8种产品的加工程序R0到R7分别存放在以SBR0,SBR1,SBR7为首地址的内存中。而这8个首地址偏移量连续存放在以BASE为首址的跳转表内。设这8种产品的编号分别为0,1,7。若现在已知目前要加工的产品编号,应如
27、何编制一段程序,利用“跳转表”的方法自动转入该种产品的加工程序? 分两个方面来讨论: (1) 如何构成跳转表? (2) 如何根据已知的编号从表中查出该种产品加工程序的入口地址,而首要的问题就是要先求出该种产品对应的表项地址。,图5-4 跳转表, 跳转表的组成如图5-4所示。表开始的第一个单元的地址称作表基地址(或表首地址)。要查找的元素在表中的地址叫表项地址。 如:加工程序R1的入口偏移地址SBR1在表中的地址(表项地址)是BASE+2。不同的加工程序对应有不同的表项地址。 表项地址相对于基地址的偏差字节数称作偏移量。如表项地址BASE+2相对于表基地址的偏移量是2。, 表的使用。根据上述分析
28、,可看出: 表项地址 = 表基地址 + 偏移量 在这里表基地址是已知的。通过分析表的结构,可看出偏移量由产品编号乘2求得,因而表项地址就可求出了。由此得到编写程序的算法思想,画出流程图如图5-5所示。,图5-5 表分支流程图,程序描述如下: DATA SEGMENT BASE DW SBR0,SBR1,SBR2,SBR3 DW SBR4,SBR5,SBR6,SBR7 BN DB ? ;产品编号 DATA ENDS STACK SEGMENT PARA STACK STACK DB 100 DUP(?) STACK ENDS COSEG SEGMENT ASSUMECSCOSEG,DSDATA,
29、SSSTACK,START PROC FAR PUSH DS MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX MOV AL,BN MOV AH,0 ADD AL,AL ;求偏移量 =产品编号2 MOV BX,OFFSET BASE ;将表首址送入BX中 ADD BX,AX ;求表项地址,MOV AX,BX ;将加工程序的入口地址送入AX中 JMP AX RET START ENDP COSEG ENDS END START,图5-6 命令键跳转表,设12个命令键的编号分别为011。跳转表中每3个单元存放着一条转移指令。分别对应着各命令键的分支转移指令。如果已获取了
30、所按下的命令键的编号X,并已将其送入寄存器AL,那么实现转向相应的命令子程序去执行的程序段可描述如下:,MOV AH,0 MOV BL,AL ADD AL,AL ADD AL,BL ;编号乘3为偏移量 MOV BX,OFFSET BASE0 ;取表首址 ADD BX,AX ;求表地址 JMP BX,从上述程序可以看出,仍然要根据已知量(编号)求表地址。由于每一指令在表中占了3个单元,所以用编号乘3得偏移量。跳转表中存放的是转移指令,所以在求出表地址后,直接转去执行表地址内的指令就可以实现分支了。,3) 根据关键字分支 这类问题形式可以多种多样,关键字可以是给定的,也可以是在表中的或是表中给出了
31、关键字的地址等等,然后根据关键字的内容分支。 例5-11 有一台主机为8台外部设备服务,对于每台外设的服务程序已经编好并已分别存放在以首址为SR0,SR1,SR7的主机的内存中,每台外设有一条联络线与主机中的寄存器相连,如图5-7所示。,图5-7 系统框图,当所有的外设没有提出服务请求时,所有联络线上均为“0”信号。当其中一台(且同一时刻只许一台)外设要求为之服务时,就在其联络线上发出“1”信号。通过查询主机寄存器中的关键字,就可以得知是哪台外设发出的服务请求。关键字与外设的对应关系如下:,编程实现时,仍是先造一张表,表内存放关键字值与其对应的外设服务程序入口地址。如图5-8所示,表的首地址为
32、BASE。,图5-8 关键字表,DATA ENDS STACK SEGMENT PARA STACK STACK DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME CSCODE,DSDATA,SSSTACK START PROC FAR PUSH DS MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX,5.4 循环程序设计,5.4.1 重复控制指令 该类指令在循环的首部或尾部确定是否进行循环。重复控制指令的目的地址必须在本指令的-128+127字节范围之内,这些指令对标志位无影响,对于串操作及数据块操作是很有用的。在80 x8
33、6中,所有循环程序结构的循环次数计数器均使用CX/ECX。重复控制指令如表5-5所示。,表5-5 重复控制指令,例5-12 在4.4.4节中讲述指令操作数基址变址寻址时,曾举了下述一个例子: XOR EAX,EAX ;EAX0 MOV EBX,OFFSET ARRAYSTR ;EBX数组ARRAYSTR的基地址 MOV ECX,LENGTH ARRAYSTR MOV ESI,0 ;变址寄存器ESI指向0 ALAB: ADD EAX,EBX+ESI ;利用基址加变址得到数组元素 ADD ESI,4 ;增加变址 LOOP ALAB ;继续,该程序中就用到了循环指令LOOP。为得到数组元素,可将数组
34、ARRAYSTR的基地址送到基址寄存器EBX中,而变址寄存器ESI初始化时先清0。每循环一次,只要修改变址寄存器ESI的内容,即将ESI增4,就可得到参与加运算的数组元素了。,例5-13 在字节数组中找出第一个非0数据。 DATA SEGMENT ARRAY DB 0,0,0,0,0,33,54,18,0,78,26,55 COUNT EQU $-ARRAY DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,START:MOV AX,DATA MOV DS,AX MOV CX,COUNT ;数据个数 MOV DI,-1 AGAIN:INC DI CMP
35、 ARRAYDI,0 ;与0比较 LOOPZ AGAIN ;相等就循环 MOV DX,DI ;非0数据的地址 MOV AH,4CH INT 21H CODE ENDS END START,说明:本程序的构成未采用过程形式描述,返回命令使用的是: MOV AH,4CH INT 21H,例5-14 在字符串中查找给定的字符,找到显示Y,否则显示N。设给定的字符在数据段中给出,例如,需查找字符A。 DATA SEGMENT STRING DB KKDKTRGPASDFJJFJ STR EQU $-STRING DATA ENDS CODE SEGMENT ASSUMECS:CODE,DS:DATA
36、START:MOV AX,DATA MOV DS,AX,MOV CX,STR MOV SI,-1 MOV AL,A ;查找数据送AL NEXT:INC SI CMP AL,STRINGSI ;比较 LOOPNE NEXT ;不相等就循环 JNZ NOTFOU ;未找到,退出 MOV DL,Y ;找到了输出字符Y MOV AH,2 INT 21H JMP QUIT,NOTFOU: MOV DL,N ;未找到输出字符N MOV AH,2 INT 21H QUIT: MOV AH,4CH INT 21H CODE ENDS END START,5.4.2 循环程序的基本结构 1循环程序的组成部分 循
37、环程序一般由四个部分组成: (1) 初始化部分:为循环做准备工作,包括建立指针,置计数器,设置其他变量的初始值等; (2) 循环体:完成循环的基本操作,是循环程序的核心部分; (3) 修改部分:修改操作数地址等,为下次循环做准备; (4) 控制部分:修改计数器,查看循环控制条件,进行循环控制。,2循环程序的基本结构形式 循环程序的基本结构形式分为“先执行,后判断”结构和“先判断,后执行”结构。 1) “先执行,后判断”结构 在这种结构中,进入循环后,先执行一次循环体后,再判断循环是否结束。对“先执行,后判断”结构而言,至少要执行一次循环体,如图5-9所示。,图5-9 “先执行,后判断”结构图,
38、例5-15 试编制一个程序统计一个数据块中负元素的个数。 问题分析:一字节带符号数中,最高位(即符号位)为1的数为负数。要统计负数个数即查看每一个数的符号位,并统计符号位为1 的个数。这是有规律地重复,因此,可用循环程序实现。,DATA SEGMENT D1 DB -1,-3,5,6,9, ;定义若干字节带符号数 RS DW ? ;存放负元素 DATA ENDS CODE SEGMENT ASSUME CSCODE,DSDATA START PROC FAR PUSH DS MOV AX,0 PUSH AX,MOV AX,DATA MOV DS,AX MOV BX,OFFSET D1 ;建数据
39、指针 MOV CX,10 ;置计数器初值 MOV DX,0 ;置结果初值,统计负数的个数值存放在DX中 LOP1:MOV AX,BX CMP AX,0 ;用AND AX,AX也可以 JGE JUS INC DX,JUS: INC BX DEC CX ;循环次数减1 JNE LOP1 MOV RS,DX RET START ENDP CODE ENDS END START,2) “先判断,后执行”结构 这种结构,进入循环后,首先判断循环结束的条件,再视判断结果决定是否执行循环体。“先判断,后执行”结构中,如果一进入循环就满足循环结束条件,循环体将一次也不执行,即循环次数为零,所以又称为“可零迭代
40、循环”,如图5-10所示。,图5-10 “先判断,后执行”结构图,例5-16 AX寄存器中有一个16位二进制数,编程序统计其中值为1的位的个数。统计结果存放在CX寄存器中。 问题分析:这个程序最好用“先判断,后执行”的结构,AX寄存器中的数为全0,则不必再作统计工作了。 从图5-10看出,一进入循环,首先是控制部分,先进行判断,再看这个程序的循环和控制部分之间有没有明显的分界。程序段描述如下:,5.4.3 多重循环 有些问题比较复杂,一重循环不够用,必须使用多重循环,这些循环是一个套一个的。双重循环程序的一般结构如图5-11所示。从图5-11中可以看出,内循环必须完整地包含在外循环中,循环可以
41、嵌套、并列,但不可以交叉,绝对不允许从外循环中直接跳到内层循环中。 注意:千万不要使循环返回到初始化部分,否则会出现死循环。,图5-11 双重循环程序框图,例5-17 编制计算矩阵向量相乘的程序。比如:,计算公式为,展开为,使用双重循环在于:计算每一个ci时,均有4项,这是一重循环,而ci共有4个,这又是一重循环。所以要完成该题,就需要使用双重循环来计算。这就是说,每一个ci的计算过程都是相同的。因此可先考虑编制出一个计算ci(先固定i=1)的程序,为便于循环,把矩阵A的元素按行依次相邻存放,即a11,a12,a13,a14,a21,a22,a23,a24,a31,把向量B和C的元素也依次相邻
42、存放,在数据段里定义数据项如下:,A DB a11,a12,a13,a14 DB a21,a22,a23,a24 DB a31,a32,a33,a34 DB a41,a42,a43,a44 B DB b1,b2,b3,b4 C DW 4 DUP(?),数据的存储分配如下: A0:a11 A4:a21 A8:a31 A12:a41 A1:a12 A5:a22 A9:a32 A13:a42 A2:a13 A6:a23 A10:a33 A14:a43 A3:a14 A7:a24 A11:a34 A15:a44 B0:b1 C0:c1 B1:b2 C2:c2 B2:b3 C3:c3 B3:b4 C4:
43、c4,其中,小写字母a11,a12,a44,b1,b4等均代表具体的无符号数。,接下来应考虑c2的计算。计算c2和计算c1有两点不同:一是数据A从a21开始,而不是a11,也就是从A4开始,即SI从4开始而不是从0开始;二是结果存放在C2中,而不是C0中。其中,第一点不同,由于在计算c1的过程中SI每次加1,到计算完c1时,SI的值恰好是4,这一要求自然满足;第二点不同,可以利用基址寄存器BX,开始给BX送初值0,每循环一次加2,利用CBX间接寻址方式访问ci的方法解决。c3、c4的计算也是一样的。,c2的计算过程可看成是新的循环的初始部分,只是循环控制部分如果都用LOOP指令,都是用CX作为
44、计数器的。因此需要将控制计算4个ci的CX的计数值暂时保存起来。比如,存放在其他此程序不用的寄存器里、一个字存储变量里或堆栈中。这里先将它暂时压入堆栈,在使用它之前(执行LOOP指令前)再从堆栈里弹出送回CX。,这里出现了一个循环程序的工作部分又是一个循环程序的情况,这就是一重循环套一重循环,称为双重循环。计算一个ci的循环叫做内循环;计算所有ci的循环叫做外循环。对于多重循环,一定要分清层次,内、外循环的四个组成部分应如何划分,一点也不能模糊。外循环的准备部分包括0SI、0BX和4CX,其中,0 SI也是内循环的准备部分;内循环的准备部分同时也是外循环的工作部分,包括PUSH CX、0DI、
45、0CBX和4CX,当外循环重复没有达到4次时,这些操作被外循环重复执行。如果不加分析地把控制转移出循环,那将是错误的。,给出一组可以进行计算的具体数据,定义在数据段以A为起始单元的区域中,计算结果存放在数组C里。为了程序简便、明了,本例没有写出二进制转化为十进制输出计算结果的指令序列。程序描述如下:,NAME EX DATA SEGMENT A DB 1,0,2,3 DB 0,1,1,0 DB 3,0,1,0 DB 4,2,0,1 B DB 0,1,1,0 C DW 4 DUP(?) TOP DB ?,DATA ENDS STACK SEGMENT PARA STACK STACK STAPN
46、 DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK START: MOV AX,DATA MOV DS,AX MOV AX,STACK MOV SS,AX MOV AX,TOP MOV SP,AX MOV SI,0 MOV BX,0 MOV CX,4,LOOP1: PUSH CX MOV DI,0 MOV WORD PTR CBX,0 MOV CX,4 LOOP2: MOV AH,0 MOV AL,ASI MUL BDI ;BDI*AL ADD CBX,AX ;CBXCBX+AX,INC SI INC D
47、I LOOP LOOP2 ADD BX,2 ;确定存放下一个ci值的地址 POP CX LOOP LOOP1 MOV AH,4CH INT 21H CODE ENDS END START,注意:当内、外重循环均要求使用CX计数器时,可先将外循环计数器CX的内容压栈,待内循环处理完后,再将外循环计数器CX的内容弹出栈,用此方法来解决内、外重循环均要求使用CX计数器而带来的冲突。,例5-18 使用冒泡排序法将内存中给定的10个带符号数按照从小到大的顺序重新排列出来。 分析:算法编制的基本思想是采用两两比较的方法:先拿an与an-1比,若anan-1,则不交换,反之则交换;然后用an-1与an-2相
48、比,按同样原则决定是否交换,这样一直比下去,最后用a2与a1相比,也按同样原则决定是否交换,当第一次大循环结束时,数组中的最小值冒到了前面。但是数组尚未按大小排列好,还要进行第二次大循环,这样,数组中的第二个最小值,也上升到顶部这样不断地循环下去,若数组的长度为n,则最多经过n-1次上述的大循环,就可以使数组按大小的次序排列整齐。在每一个大循环中,数两两比较的次数,在第一次大循环时为n-1;在第二次大循环时为n-2;,依据上述基本思想就可以写出程序了。但在实际中,有的数组不需要经过n-1次大循环就已经排列整齐了。也就是说,按上述基本思想写出的程序,有时存在不必要的循环,这样就使得程序的效率不高
49、。为了在程序中消除不必要的循环,可设置一个标志,在每次大循环开始时,置此标志为0;若在整个大循环中,两两比较后,发生过交换,则置此标志为-1。然后在下一次大循环开始时,检查此标志,若不为0,表示数组未排列好,继续进行循环;若为0,则表示数组已按大小次序排列好(每次两两比较时,都是大的数在后,小的数在前,故不用交换),就停止循环。下面给出的程序就是按这一思想编制的。,SORT_DA SEGMENT ARRAY DW 1273,5346,0FF7FH,1080H,0FFFFH,55ABH DW 3069H,5C00H,56DEH,0B69H COUNT EQU $-ARRAY SORT_DA EN
50、DS STACK SEGMENT PARA STACK STACK DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME CSCODE,DSSORT_DA ASSUME ESSORT_DA,SSSTACK,STR PROC FAR START:PUSH DS MOV AX,0 PUSH AX MOV AX,SORT_DA MOV DS,AX MOV ES,AX MOV BL,0FFH A1: CMP BL,0 JE A4 XOR BL,BL MOV CX,COUNT SHR CX,1 DEC CX XOR SI,SI,A2: MOV AX,ARRAYSI C
51、MP AX,ARRAYSI+2 JLE A3 XCHG ARRAYSI+2,AX MOV ARRAYSI,AX MOV BL,0FFH A3: INC SI INC SI LOOP A2 JMP A1 A4: RET STR ENDP CODE ENDS END START,需要指出的是,排序是一种非常有用的操作,第9章介绍的折半查找,其前提就是要求表格中的数据(或字符)的排列是有次序的。例如,对于数字,要求它按数字的大小排列,字符则按其ASCII码值的大小排列。于是对一个无次序的表来讲,要对其进行折半查找首先要使用某种排序方法,如使用冒泡法对其进行排序。,2按条件控制循环 有些情况下,计数次
52、数无法事先确定,但循环次数与问题中的某些条件有关,这些条件可以检测到,这时采用条件控制循环为好。 使用条件:循环次数与某些条件有关,条件可以检测。 使用形式:检测、比较、判断。 例5-16中,统计寄存器AX中值为“1”的个数就属于这种类型的问题。,因为事先并不知道寄存器的情况,若是全零,已不必再循环;若仅最高位为1,则移位一次即可;若最低位为1,则要移位16次,即循环16次。循环结束的条件就看寄存器中是否为全0,当然对于这个问题来说,也可以采用计数方法。无论什么情况,都强迫循环进行16次,不过当AX为零时,循环16次统计结果,“1”的个数仍为0。这样的问题采用计数方法控制就显得太不合适了。,例
53、5-19 求一个数的平方根。 用减奇数次数的方法,求出一个数的近似平方根,这个平方根是一个整数。如求17的平方根,可以用17相继减去奇数1,3,5,7,当结果为负时停止,即 17-1-3-5-7-90 可以看出,17在减了5次奇数后结果变负。可近似认为17的平方根为4或5。在下面的程序中,结果将为4。,DATA SEGMENT NUM DW 1CE4H ANS DW ? DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START:MOV AX,DATA MOV DS,AX MOV DX,0H ;存放结果的寄存器清0 MOV CX,01H ;置第1个奇
54、数,AGAIN: SUB NUM,CX ;减去奇数 JL TOEND ;奇数已比持续相减的结果大,退出 INC DX ;减一次奇数,结果增1 ADD CX,02H ;生成下一个奇数 JMP AGAIN TOEND:MOV ANS,DX ;平方根送ANS中 MOV AH,4CH INT 21H CODE ENDS END START,3用逻辑尺控制循环 用户输入的一串代码,称为逻辑尺,作为分支的依据。 例5-20 某程序所需的12个参数值存于BUFFER起始的缓冲区中,对BUFFER中的第1、2、5、7、10个参数值需调用函数Y2*X进行计算,对BUFFER中的第3、4、6、8、9、11、12个
55、参数值需调用函数Y4*X进行计算。现要求使用一逻辑尺编程实现对BUFFER中的参数处理。,问题分析:由于分支的条件不规则,可建立一个与要求相对应的逻辑尺(位串)0011010110110000来作为控制循环的条件。程序如下: DATA SEGMENT BUFFER DW 101,202,33,44,55,66,77,808,909,123,678,987;X的值 BLOCK DW 12 DUP(?) ;Y的保留单元 LOGRUL EQU 0011010110110000B ;前12位为逻辑尺,顺序为1、2、12 DATA ENDS CODE SEGMENT ASSUME CSCODE,DSDA
56、TA,STARTPROC FAR PUSH DS MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX MOV DX,LOGRUL ;逻辑尺DX MOV CX,12 LEA BX,BUFFER ;BX指向X LEA SI,BLOCK ;SI指向Y,AGAIN:MOV AX,BX ;取X RCL DX,1 ;逻辑尺左移一位CF JC ANOTH ;CF=1,转ANOTH CALL FUN1 ;CF=0,调用FUN1 NEXT: MOV SI,AX ;保存Y INC BX INC BX ;指向下一个X INC SI INC SI ;指向下一个Y LOOP AGAIN RET,START ENDP CODE ENDS END START ANOTH:CALL FUN2 ;调用FUN2 JMP NEXT FUN1 PROC ;Y2X ADD AX,AX RET FUN1 ENDP FUN2 PROC ;Y4X ADD AX,AX ADD AX,AX RET FUN2 ENDP,关于逻辑尺的讨论: (1) 逻辑尺既不是参与运算的常数,又不是指令,而是判断分支的依据,犹如尺子可以描述布的长短一样。 (2)
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 汽车配件采购制度流程
- 加强食堂采购管理制度
- 小学采购申报制度
- 化工料采购管理制度范本
- 医院基建办采购招标制度
- 数字化赋能:采油厂油藏经营效益评价与预测系统构建与实践
- 银行教育培训工作总结(2篇)
- 医疗卫生招聘复习题复习题-口腔医学及答案
- 数字化浪潮下伊利股份转型之路:路径探索与绩效解析
- 数字化浪潮下JS银行安徽分行电子银行业务风险管理的困境与突破
- 膝关节骨折脱位课件
- 临期药品行业市场细分消费者需求变化分析报告
- 《水力学》课件(共十一章)
- 工厂安全风险评估与整改措施报告
- 浙江空调管理办法
- 银行架构管理办法
- 小学动感中队活动方案
- 猪群周转培训课件
- 购物中心节能管理制度
- 《中国传统文化》课件:佛教思想及其人生模式
- 《AIGC应用实战:写作、绘图、视频制作、直播》全套教学课件
评论
0/150
提交评论