最新消息:非无江海志,潇洒送日月

汇编中的分支与循环

编程开发 江海志 5074浏览 0评论

在高级语言程序设计中,我们不仅要求程序能够顺序执行,还要求其能够执行不同分支,或者重复执行某些操作。在汇编语言中,同样具有相同的能力,可以由相应的指令来控制程序的执行流程。因为毕竟高级语言都是汇编实现的,只是不直观而已。

转移

无条件转移为JMP,类似c语言中的 goto。在c语言中,goto是不推荐使用的,会使程序难以控制和理解,但在汇编中,没有相应的if while语句,只能使用 JMP来完成逻辑复杂的控制结构。

JMP分为段内转移和段间转移,不过在windows编程上,一个代码段的大小为 4GB,一般不需要段间转移。

 

条件转移是由CPU提供多个标志位,在执行这类指令之前,检查之前已经设置的标志位的状态来决定是否进行转移。可以想象,通过条件转移,可以实现分支和循环。

 

分支

在汇编中,设计一个分支结构的思路基本是这样的,由条件转移指令来判断条件是否满足,若满足,则跳过分支程序,否则继续向下执行,最后会合。

x的符号

c 程序

  1. </pre><pre name=“code” class=“cpp”>int x = 10;
  2. int signX;
  3. int main()
  4. {
  5.     if (x > 0)
  6.         signX = 1;
  7.     else if (x == 0)
  8.         signX = 0;
  9.     else
  10.         signX = -1;
  11. }

 

对应的汇编

  1. x                SDWORD        -45
  2. signX        SDWORD        ?
  3. mov signX, 0
  4. cmp        x, 0
  5. jz        a70                                ;为0
  6. jg        a60                                ;大于0
  7. mov signX, -1
  8. jmp        a70
  9. a60:
  10. mov        signX, 1
  11. a70:

 

 

二分查找

对于一个已排序的数组,最快的查找一个元素的方法不是顺序查找,而是二分查找。这个用c 语言写非常方便。首先我们还是画出流程图,这有助于我们用 JMP转换之。

数组为R,元素个数为n,要查找的数为a。

执行过程为:
1)先设定查找范围为下界 l到上界h

2)如果下界大于上界,且没查到,则查找失败

3)取下界和上界的中点m=(l+h)/2,该数为R[m]

4)若该数为a,则算法结束

5)若该数小于a,则修改下界l=m+1,并跳转到第二步

6)若该数大于a,则修改上界 h=m-1,并跳转到第二步

汇编源码

  1. .386
  2. .model flat,stdcall
  3. optioncasemap:none
  4. includelib  msvcrt.lib
  5. printf      PROTO C :dword, :vararg
  6. .data
  7. dArray      dword 50, 56, 64, 73, 83, 92, 103, 105
  8. ITEMS       equ    $-dArray)/4        ; 数组长度
  9. Index           dword   ?
  10. Count           dword   ?
  11. szFmt           byte    ‘Index=%d Count=%d Element=%d’, 0;  格式化字符串
  12. szErr           byte    ‘Not Found’
  13. l       sdword ?
  14. h       sdword ?
  15. m       sdword ?
  16. a       sdword 83      ; 所查找的数字
  17. .code
  18. start:
  19.     mov    Index, -1;
  20.     mov    Count,  0
  21.     mov    l, 0
  22.     mov    h, ITEMS-1;
  23.     mov    ecx, l
  24.     mov    edx, h
  25. b10:
  26.     cmp    ecx, edx    ; 下界和上界比较
  27.     jg     b40         ; 超出,则未找到
  28.     mov    esi, ecx;
  29.     add    esi, edx
  30.     shr    esi, 1      ; m = (l+h)>>1
  31.     inc    Count
  32.     mov    eax, a      ; 取出被比较的数
  33.     cmp    eax, dArray[esi*4] ; 取出该数
  34.     jz     b30         ; 相等
  35.     jg     b20         ; 比 a 小,修改下界
  36.     ; 比 a 大,修改上界
  37.     mov    edx, esi
  38.     dec    edx         ; h = m-1
  39.     jmp    b10
  40. b20:
  41.     mov    ecx, esi
  42.     inc    ecx         ; l = m+1
  43.     jmp    b10
  44. b30:
  45.     mov    Index, esi
  46.     ; 调用 c 库的 printf
  47.     invoke printf, offset szFmt, IndexCount, dArray[esi*4]
  48.     jmp    b50
  49. b40:
  50.     ; 查找失败
  51.     invoke printf, offset szErr
  52. b50:
  53.     ret
  54. end start

 

 

有序表插入

首先遍历数组,找到m,满足 R[m-1]<a && R[m]>a。然后将R[m..n-1]每一个元素向后移动一个位置,再插入a。

关键部分汇编代码

  1. start:
  2.     mov    eax, Element        ; 将插入的元素
  3.     mov    esi, 0              ; 当前比较元素的下标
  4. c10:
  5.     cmp    dArray[esi*4], eax
  6.     ja     c20
  7.     inc    esi
  8.     cmp    esi, ITEMS          ; 超出数组长度,
  9.     jb     c10                 ; 没有超出
  10.     ;
  11. c20:
  12.     mov    edi, ITEMS-1        ; 从数组尾部开始移动
  13. c30:
  14.     cmp    edi, esi            ; 与插入位置比较
  15.     jl     c40                 ; 移动完成
  16.     ; 还没完成,继续移动
  17.     mov    ebx, dArray[edi*4]
  18.     mov    dArray[edi*4+4], ebx ; 向后移动一个元素
  19.     dec    edi
  20.     jmp    c30                 ; 继续判断,决定是否移动
  21. c40:
  22.     mov    dArray[esi*4], eax  ; 移动完成,插入元素

 

 

循环

实际编码解决问题时,经常遇到大量重复的动作,利用循环结构可以简化程序的编写。实际在上文中,已经有循环部分出现,但那是不定次数的循环。

一个循环结构分为三部分:循环初始化,循环体,循环结束控制。

一个固定次数的循环,有LOOP命令来实现,循环次数在ECX中。

 

阶乘

这里直接使用ECX 作为乘数,其会随循环递减,正好符合阶乘的定义。

  1. .data
  2.     Fact   dword   ?
  3.     N      EQU     5
  4. .code
  5. start:
  6.     mov    ecx, N      ; 循环初值,同时也是乘数初值
  7.     mov    eax, 1      ; fact 初值
  8. e10:
  9.     imul   eax, ecx    ; 相乘
  10.     loop   e10
  11.     mov    Fact, eax   ; 阶乘结果

 

 

冒泡

冒泡算法是一个多重循环。

  1. start:
  2.     mov    ecx, ITEMS-1
  3. i10:
  4.     xor    esi, esi    ; 清零
  5. i20:
  6.     mov    eax, dArray[esi*4]
  7.     mov    ebx, dArray[esi*4+4]
  8.     cmp    eax, ebx    ; 比较相邻的两个数
  9.     jl     i30
  10.     mov    dArray[esi*4], ebx    ;较大的数在左边,需要移动
  11.     mov    dArray[esi*4+4], eax  ; 需要交换两个数
  12. ; 比较下一对数
  13. i30:
  14.     inc    esi
  15.     cmp    esi, ecx
  16.     jb     i20     ; 内循环,一遍下来,最大的数,移到了最右边
  17.     loop   i10     ; 外循环

参考文献

[1] 谭毓安, 张雪兰. Windows汇编语言程序设计教程. 北京: 电子工业出版社, 2005.

转载请注明:江海志の博客 » 汇编中的分支与循环

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址