龙空技术网

5.13 汇编语言:仿写For循环语句

聪明芒果yO 28

前言:

眼前小伙伴们对“c语言for循环排序”可能比较重视,朋友们都需要学习一些“c语言for循环排序”的相关知识。那么小编同时在网络上收集了一些对于“c语言for循环排序””的相关文章,希望兄弟们能喜欢,朋友们一起来了解一下吧!

循环语句(for)是计算机编程中的一种基本控制结构,它允许程序按照指定的次数或范围重复执行一段代码块。for循环在处理需要进行迭代操作的情况下非常有用,它使得程序可以更加方便地控制循环的次数。一般来说,for循环由三个部分组成:初始化部分、条件表达式和更新部分,以及一个需要重复执行的代码块。在每次循环迭代开始时,程序首先执行初始化部分,然后检查条件表达式的值,如果为真,则执行代码块,并在每次循环结束后执行更新部分。只要条件表达式为真,for循环就会一直重复执行;一旦条件表达式为假,循环将停止,程序继续执行循环之后的代码。

11.14 FOR 循环结构优化

For语句先初始化条件变量,然后在判断是否符合条件,符合则执行循环体,不符合则跳过执行。For循环结构的效率最低,该语句的构建往往需要三个跳转来实现,首先需要初始化变量此处要进行一次判断,其次是内部循环体需要另一个判断通常用于实现跳出循环体,最后一步则需要一个无条件跳转指令跳回到循环首地址,但在开启了O2优化时编译器也会尽可能将其转换为While语句,如果可以还会继续将While转为带有IF语句的Do循环来提高执行效率。

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.lib.data  count DWORD ?.code  main PROC    mov dword ptr ds:[count],0          ; 设置 int x = 0;    jmp L2  L1:    mov eax,dword ptr ds:[count]        ; x = x++    add eax,1    mov dword ptr ds:[count],eax  L2:    cmp dword ptr ds:[count],10         ; 比较 x < 10    jge lop_end        xor eax,eax                         ; 执行循环体    jmp L1      lop_end:    int 3    invoke ExitProcess,0  main ENDPEND main

虽然For语句在执行效率上来说是最低的,但该语句的使用确是最符合我们思维方式的,在高级语言中应用最为广泛,例如在Python中For循环体被简化成了for x in range(2,10)它可以指定一个循环范围,该语句利用汇编完全也可以被构建出来,我们接着尝试构建一下这个特别的循环体。

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.lib.data  start_count DWORD ?  end_count DWORD ?.code  main PROC    mov dword ptr ds:[start_count],2     ; 指定开始循环编号    mov dword ptr ds:[end_count],5       ; 指定结束循环编号        mov ecx,dword ptr ds:[start_count]  L1:    cmp dword ptr ds:[end_count],ecx    jle lop_end        xor eax,eax                          ; 循环体内部        add ecx,1                            ; 每次递增    mov dword ptr ds:[start_count],ecx    jmp L1      lop_end:    int 3    invoke ExitProcess,0  main ENDPEND main
11.20 仿写For水仙花数

该C++代码实现了水仙花数的查找算法,水仙花数是指一个三位数,它的每个位上的数字的立方和等于它本身。在循环中,遍历100~999之间的每一个数,将其分解为三个数(百、十、个位),再将三个数分别平方并相加,判断与原数是否相等,如果相等则输出该数即为水仙花数。

• 例如: 153是一个水仙花数,因为153等于1的3次方加上5的3次方加上3的3次方

#include <stdio.h>#include <Windows.h>int main(int argc, char *argv[]){  int x, y, z, n;  for (n = 100; n < 1000; n++)  {    x = n / 100;          // 分解百位    y = n / 10 % 10;      // 分解十位    z = n % 10;           // 分解个位    if (x * 100 + y * 10 + z == x*x*x + y*y*y + z*z*z)    {      printf("水仙花: %-5d \n", n);    }  }  system("pause");  return 0;}

尝试使用汇编实现计算逻辑,这段代码没有任何难度,因为并不会涉及到嵌套循环的问题,只是在计算四则运算时需要格外注意些。

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib.data  x DWORD ?  y DWORD ?  z DWORD ?  n DWORD ?  szFmt BYTE '水仙花: %-5d ',0dh,0ah,0.code  main PROC    mov dword ptr ds:[n],100     ; n = 100    jmp L1  L2: mov eax,dword ptr ds:[n]    add eax,1                    ; n++    mov dword ptr ds:[n],eax  L1: mov eax,dword ptr ds:[n]    cmp eax,1000                 ; n < 1000    jge lop_end        mov eax,dword ptr ds:[n]    cdq    mov ecx,100                  ; x = n / 100;    idiv ecx    mov dword ptr ds:[x],eax        mov eax,dword ptr ds:[n]    cdq    mov ecx,10    idiv ecx                     ; y = n / 10;    cdq    mov ecx,10    idiv ecx                     ; y = y % 10;    mov dword ptr ds:[y],edx        mov eax,dword ptr ds:[n]    cdq    mov ecx,10    idiv ecx                     ; z = n % 10;    mov dword ptr ds:[z],edx        ; 开始执行if()比较语句    imul eax,dword ptr ds:[x],100  ; x * 100    imul ecx,dword ptr ds:[y],10   ; y * 10    add eax,dword ptr ds:[z]       ; + z    add ecx,eax        mov edx,dword ptr ds:[x]    imul edx,dword ptr ds:[x]      ; x*x*x    imul edx,dword ptr ds:[x]        mov eax,dword ptr ds:[y]    imul eax,dword ptr ds:[y]      ; y*y*y    imul eax,dword ptr ds:[y]    add edx,eax        mov eax,dword ptr ds:[z]    imul eax,dword ptr ds:[z]      ; z*z*z    imul eax,dword ptr ds:[z]    add edx,eax        cmp ecx,edx   ; (x * 100 + y * 10 + z) == (x*x*x + y*y*y + z*z*z)    jne L2        mov eax,dword ptr ds:[n]    invoke crt_printf,addr szFmt,eax    jmp L2      lop_end:    int 3   main ENDPEND main
11.21 For循环尝试判断

该C++代码实现了一个简单的循环,遍历数组中的所有元素并输出大于等于50的元素。在循环中,通过判断Array数组中每个元素与50的大小关系,如果元素大于等于50,则使用printf函数输出该元素的值。最终程序输出所有大于等于50的元素。

#include <stdio.h>#include <Windows.h>int main(int argc,char *argv[]){  int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };  for (int x = 0; x < 10; x++)  {    if (Array[x] >= 50)    {      printf("out -> %d \n", Array[x]);    }  }  return 0;}

上述C语言代码如果通过汇编语言实现可以写成如下样子,读者可自行理解流程;

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib.data  MyArray DWORD 56,78,33,45,78,90,32,44,56,67  count DWORD ?  szFmt BYTE 'out -> %d ',0dh,0ah,0.code  main PROC        mov dword ptr ds:[count],0      ; int x = 0    jmp L1  L2: mov eax,dword ptr ds:[count]    add eax,1                       ; x ++    mov dword ptr ds:[count],eax  L1:    cmp dword ptr ds:[count],10     ; x < 10    jge lop_end        mov eax,dword ptr ds:[count]          ; 获取循环次数,当作因子    lea esi,dword ptr ds:[MyArray]        ; 取数组基地址    mov ebx,dword ptr ds:[esi + eax * 4]  ; 因子寻址    cmp ebx,50    jl L2                                 ; 如果小于50则跳转到下一次循环        invoke crt_printf,addr szFmt,ebx      ; 调用系统crt    jmp L2  lop_end:    int 3      invoke ExitProcess,0  main ENDPEND main

在读者学会了上述代码编写之后,我们继续增加代码的复杂度,如下所示代码实现了对整型数组的最大值、最小值、元素总和以及平均值的计算。在循环中,通过依次遍历数组中的每一个元素,维护一个当前最大值max_result和最小值min_result,并对元素进行累加求和,最终计算出数组中所有元素的平均值avg_result。代码中使用printf函数输出求得的四个值(max、min、sum、avg),并使用system函数暂停程序以便观察输出结果。

#include <stdio.h>#include <Windows.h>int main(int argc, char *argv[]){  int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };  int max_result = 0,min_result = 100,sum_result = 0,avg_result = 0;  for (int x = 0; x < 10; x++)  {    if (Array[x] >= max_result)    {      max_result = Array[x];    }    if (Array[x] <= min_result)    {      min_result = Array[x];    }    sum_result = sum_result + Array[x];    avg_result = sum_result / 10;  }  printf("max = %d min = %d sum = %d avg = %d \n", max_result,min_result,sum_result,avg_result);  system("pause");  return 0;}

上述代码读者可尝试使用汇编语言来实现一下,如下代码是笔者思考后编写出来的实现流程,读者可自行对照参考;

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib.data  MyArray DWORD 56,78,33,45,78,90,32,44,56,67  count DWORD ?  max_result DWORD 0  min_result DWORD 100  sum_result DWORD 0  avg_result DWORD 0    szFmt BYTE 'max = %d min= %d sum= %d avg = %d ',0dh,0ah,0.code  main PROC    mov dword ptr ds:[count],0      ; int x = 0    jmp L1  L2: mov eax,dword ptr ds:[count]    add eax,1                       ; x ++    mov dword ptr ds:[count],eax  L1:    cmp dword ptr ds:[count],10     ; x < 10    jge lop_end    mov eax,dword ptr ds:[count]    lea esi,dword ptr ds:[MyArray]        mov ebx,dword ptr ds:[esi + eax * 4]    cmp ebx,dword ptr ds:[max_result]      ; Array[x] >= max_result    jl L3    mov dword ptr ds:[max_result],ebx      ; max_result = Array[x];  L3:    mov ebx,dword ptr ds:[esi + eax * 4]    cmp ebx,dword ptr ds:[min_result]      ; Array[x] <= min_result    jg L4    mov dword ptr ds:[min_result],ebx  L4:    mov ebx,dword ptr ds:[esi + eax * 4]   ; Array[x]    add dword ptr ds:[sum_result],ebx      ; sum_result = sum_result + Array[x];        mov eax,dword ptr ds:[sum_result]    cdq                                    ; 符号扩展    mov ecx,10                             ; / 10    idiv ecx                               ; sum_result / 10;    mov dword ptr ds:[avg_result],eax      ; avg_result    jmp L2      lop_end:    mov eax,dword ptr ds:[max_result]    mov ebx,dword ptr ds:[min_result]    mov ecx,dword ptr ds:[sum_result]    mov edx,dword ptr ds:[avg_result]    invoke crt_printf,addr szFmt,eax,ebx,ecx,edx    int 3  main ENDPEND main
11.22 For循环多重IF判断

该C++代码实现了对两个数组进行元素相加,并输出相加结果的奇偶性。在循环中,对SrcArrayDstArray两个数组中的元素相加,如果两个元素均不为0,则判断相加的结果是否为偶数,如果是,则使用printf函数输出偶数sum的形式,否则输出基数sum的形式。其中sum表示两个元素相加的结果。代码中使用system函数暂停程序以便观察输出结果。

#include <stdio.h>#include <Windows.h>int main(int argc, char *argv[]){  int SrcArray[10] = { 56,78,33,45,78,90,32,15,56,67 };  int DstArray[10] = { 59,77,89,23,11,45,67,88,93,27 };  int index = 0;  for (index = 0; index < 10; index++)  {    if (SrcArray[index] != 0 && DstArray[index] != 0)    {      int sum = SrcArray[index] + DstArray[index];      if (sum % 2 == 0)        printf("偶数: %d \n", sum);      else        printf("基数: %d \n", sum);    }  }  system("pause");  return 0;}

上述代码片段的逻辑并不复杂,仅仅只是循环内部嵌套双层判断,笔者思考片刻后即写出了与之对应的汇编代码;

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib.data  SrcArray DWORD 56,78,33,45,78,90,32,15,56,67  DstArray DWORD 59,77,89,23,11,45,67,88,93,27  index DWORD 0  sum DWORD 0    szFmt1 BYTE '基数: %d ',0dh,0ah,0  szFmt2 BYTE '偶数: %d ',0dh,0ah,0.code  main PROC    mov dword ptr ds:[index],0        ; index = 0        jmp L1  L2: mov eax,dword ptr ds:[index]    add eax,1                         ; index++    mov dword ptr ds:[index],eax  L1:    cmp dword ptr ds:[index],10       ; index < 10    jge lop_end        mov eax,dword ptr ds:[index];    cmp dword ptr ds:[SrcArray + eax * 4],0    je L2                                     ; SrcArray[index] != 0        mov eax,dword ptr ds:[index]    cmp dword ptr ds:[DstArray + eax * 4],0   ; DstArray[index] != 0    je L2        ; ------------------------------------------    ; 另类加法,通过一个SrcArray定位DstArray完成加法        mov eax,dword ptr ds:[index]                 ; 获取因子    lea esi,dword ptr ds:[SrcArray]              ; 取数组首地址        mov ebx,dword ptr ds:[esi + eax * 4]         ; 获取 SrcArray[index]    mov ecx,dword ptr ds:[esi + eax * 4 + 40]    ; 获取 DstArray[index]    add ebx,ecx                                  ; SrcArray[index] + DstArray[index]    mov dword ptr ds:[sum],ebx                   ; sum = SrcArray[index] + DstArray[index]        mov eax,dword ptr ds:[sum]    and eax,080000001h                           ; sum % 2 == 0    test eax,eax    jne L3        invoke crt_printf,addr szFmt2,dword ptr ds:[sum]  ; 偶数输出    jmp L2  L3:    invoke crt_printf,addr szFmt1,dword ptr ds:[sum]  ; 基数输出    jmp L2  lop_end:    int 3  main ENDPEND main
11.23 For嵌套乘法口诀表

该C++代码实现了乘法口诀表的打印。在两个for循环中,分别对x和y进行遍历,对每一次的遍历输出一个乘法口诀表的元素。代码中使用printf函数实现输出,并使用\n进行换行。程序遍历打印了从11到99的所有乘积的结果,这就是乘法口诀表。

#include <stdio.h>#include <Windows.h>int main(int argc, char *argv[]){  for (int x = 1; x < 10; x++)  {    for (int y = 1; y <= x; y++)    {      int result = x*y;      printf("%d*%d=%-3d", y, x, result);    }    printf("\n");  }  system("pause");  return 0;}

乘法口诀表的实现方法只需要嵌套两层FOR循环语句,在使用汇编语言实现之前我们可以先来构建出这个双层循环体,如下代码所示;

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib.data  x DWORD ?  y DWORD ?  szFmt BYTE '内层循环: %d 外层循环: %d ',0dh,0ah,0  szPr  BYTE '----->',0dh,0ah,0.code  main PROC    mov dword ptr ds:[x],1        ; int x = 1    jmp L1  L2: mov eax,dword ptr ds:[x]    add eax,1                     ; x++    mov dword ptr ds:[x],eax  L1:     cmp dword ptr ds:[x],10       ; x < 10    jge lop_end    mov dword ptr ds:[y],1        ; y = 1    jmp L3  L5: mov eax,dword ptr ds:[y]    add eax,1                     ; y++    mov dword ptr ds:[y],eax  L3:    mov eax,dword ptr ds:[y]    cmp eax,dword ptr ds:[x]      ; y <= x    jg L4        ; 执行的是循环体内部    mov eax,dword ptr ds:[x]    mov ebx,dword ptr ds:[y]    invoke crt_printf,addr szFmt,eax,ebx        jmp L5  L4:    ; 执行外层循环    invoke crt_printf,addr szPr    jmp L2  lop_end:    int 3  main ENDPEND main

当有了双层循环体结构之后,我们只需要再其循环之上增加一个乘法计算功能即可,完整的计算流程如下所示;

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib.data  x DWORD ?  y DWORD ?  szFmt BYTE '%d * %d = %d ',0  szPr  BYTE ' ',0dh,0ah,0.code  main PROC    mov dword ptr ds:[x],1        ; int x = 1    jmp L1  L2: mov eax,dword ptr ds:[x]    add eax,1                     ; x++    mov dword ptr ds:[x],eax  L1:     cmp dword ptr ds:[x],10       ; x < 10    jge lop_end    mov dword ptr ds:[y],1        ; y = 1    jmp L3  L5: mov eax,dword ptr ds:[y]    add eax,1                     ; y++    mov dword ptr ds:[y],eax  L3:    mov eax,dword ptr ds:[y]    cmp eax,dword ptr ds:[x]      ; y <= x    jg L4        ; 执行的是循环体内部    mov eax,dword ptr ds:[x]    imul eax,dword ptr ds:[y]    invoke crt_printf,addr szFmt,dword ptr ds:[y],dword ptr ds:[x],eax        jmp L5  L4:    ; 执行外层循环    invoke crt_printf,addr szPr    jmp L2  lop_end:    int 3  main ENDPEND main
11.24 For语句冒泡排序

该C++代码实现了冒泡排序算法对整型数组进行排序。在冒泡排序算法中,数组中每两个相邻的元素,如果前一个元素大于后一个元素,则交换这两个元素的位置。循环遍历数组多次,每次将未排序的最大值向数组末尾冒泡,直到数组中的所有元素都排好序。代码中使用两层for循环实现排序,内层循环从数组末尾开始,逐步向前遍历,交换相邻的两个元素。外层循环控制排序的遍历次数,只有在当前相邻两个数未排序时才进行交换。程序最终输出排序后的数组。

#include <stdio.h>#include <Windows.h>int main(int argc, char *argv[]){  int Array[10] = { 34,78,65,77,89,43,23,55,67,8 };  int x, y, temporary, ArraySize=10;  for (x = 0; x < ArraySize - 1; x++)  {    for (y = ArraySize - 1; y > x; y--)    {      if (Array[y - 1] > Array[y])      {        temporary = Array[y - 1];        Array[y - 1] = Array[y];        Array[y] = temporary;      }    }  }  for (int x = 0; x < 10; x++)  {    printf("%d \n", Array[x]);    system("pause");  return 0;}

由于冒泡排序牵扯到了数据交换所以汇编版本可能稍显负责,不过大体框架还是没有脱离二层循环,仅仅只是在二层循环内部增加了一个判断流程而已,其实如果认真构建相信读者也可以很容易的写出来。

  .386p  .model flat,stdcall  option casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib.data  Array DWORD 34,78,65,77,89,43,23,55,67,8  x DWORD ?  y DWORD ?  Temporary DWORD ?  ArraySize DWORD ?  szFmt BYTE '%d --> %d ',0dh,0ah,0.code  main PROC    ; 初始化的部分    mov dword ptr ds:[x],0            ; x=0    mov dword ptr ds:[ArraySize],10   ; ArraySize=10        ; 外层循环体    jmp L1  L2: mov eax,dword ptr ds:[x]    add eax,1                          ; x++    mov dword ptr ds:[x],eax      L1: mov eax,dword ptr ds:[ArraySize]    sub eax,1                          ; ArraySize - 1    cmp dword ptr ds:[x],eax           ; x < ArraySize    jge lop_end        ; 内层循环体内容    mov eax,dword ptr ds:[ArraySize]    sub eax,1    mov dword ptr ds:[y],eax        jmp L3  L4: mov eax,dword ptr ds:[y]    sub eax,1                           ; y--    mov dword ptr ds:[y],eax    L3: mov eax,dword ptr ds:[y]    cmp eax,dword ptr ds:[x]            ; Array[y - 1] > Array[y]    jle L2        ; 寻址y和y-1的位置    mov esi,dword ptr ds:[y]    mov ebx,dword ptr ds:[Array + esi * 4]         ; Array[y]    mov edx,dword ptr ds:[Array + esi * 4 - 4]     ; Array[y - 1]    cmp edx,ebx    jle L4        ; 数据交换    mov dword ptr ds:[Array + esi * 4],edx         ; Array[y] = Array[y - 1]    mov dword ptr ds:[Array + esi * 4 - 4],ebx     ; Array[y - 1] = Array[y]    ; invoke crt_printf,addr szFmt,ebx,edx        jmp L4    jmp L2  lop_end:    nop    ; 执行打印函数    mov dword ptr ds:[Temporary],0    jmp L5  L7: mov eax,dword ptr ds:[Temporary]    add eax,1    mov dword ptr ds:[Temporary],eax  L5:    mov eax,dword ptr ds:[Temporary]    cmp eax,10    jge L6        lea esi,dword ptr ds:[Array]                ; 取数组基地址    mov esi,dword ptr ds:[Array + eax * 4]      ; 比例因子寻址    invoke crt_printf,addr szFmt,esi,esi    jmp L7  L6:    int 3  main ENDPEND main

至此,汇编中的循环结构仿写就告一段落了,笔者提醒大家,由于汇编难度较大,且代码都是线性的,所以在编写之前要分析好主次关系,当有了主次关系之后,我们就需要静下心来,一个个构建,由外到内步步为营,其实汇编也并不是那么可怕。

本文作者: 王瑞 本文链接: 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

标签: #c语言for循环排序 #c语言连续两个for #python的for语句怎么写 #用for语句排序