文章目录
- 1. `loop` 指令是什么?
- 2. 执行流程(结合你给的例子)
- 3. 和 `dec/jne` 写法的对比
- 小结对比
- 4. 使用 `loop` 的基本套路
- 5. 细节与注意点
- 6. 对应到高级语言的类比
#include<iostream>usingnamespacestd;intmain(){_asm{xoreax,eax mov ecx,0xAflag:inc eax dec ecx cmp ecx,0jne flag}_asm{xoreax,eax mov ecx,0xAflag2:inc eax loop flag2}return0;}1.loop指令是什么?
loop是 x86 汇编里的计数型循环指令,用来配合计数寄存器(16 位用CX,32 位模式常用ECX)实现“执行某段代码 N 次”的功能。
语法格式:
loop 标号含义:执行到loop 标号时,
- 先让
CX/ECX减 1; - 如果结果不为 0,则跳转到
标号处继续执行; - 如果结果为 0,则不跳转,顺序执行后面的指令。
2. 执行流程(结合你给的例子)
你第二段内联汇编:
_asm{xoreax,eax;eax=0,用来累加 mov ecx,0xA;ecx=10,循环次数 flag2:inc eax;循环体:eax++loop flag2;ecx--,如果 ecx!=0,跳回 flag2}CPU 执行过程大致是:
- 先顺序执行到
flag2:,inc eax执行一次; - 执行
loop flag2:ecx = ecx - 1- 判断
ecx是否为 0- 不为 0 → 跳回
flag2,再执行inc eax - 为 0 → 不跳转,继续执行后面的代码
- 不为 0 → 跳回
因此,上面代码会把eax从 0 加到 10,一共执行 10 次inc eax,和你第一段用dec ecx+cmp+jne实现的效果是一样的。
3. 和dec/jne写法的对比
你第一段代码:
_asm{xoreax,eax mov ecx,0xAflag:inc eax dec ecx cmp ecx,0jne flag}逻辑是:
ecx初始化为 10;- 每次循环:
inc eaxdec ecxcmp ecx, 0jne flag(如果不为 0 就跳转)
使用loop后,相当于把下面三条:
dec ecx cmp ecx, 0 jne flag压缩成了一条:
loop flag小结对比
- 功能:两种写法功能等价,都是“让
ecx从 N 递减到 0,每次执行一次循环体”。 - 代码量:
loop更简洁,一条指令代替三条。 - 语义更清晰:一看就知道这是“循环 N 次”的结构。
4. 使用loop的基本套路
典型写法(8086/32 位都类似,只是寄存器名不同):
mov cx, 循环次数 ; 或 mov ecx, ... start: ; 循环体代码 ; ... loop start三要素:
- 预先给 CX/ECX 赋值:循环次数;
- 定义一个标号:循环体的起始位置(如
start:); - 在标号和
loop之间写循环体:这段代码会被重复执行。
5. 细节与注意点
计数寄存器固定
- 16 位模式:使用
CX - 32 位模式:使用
ECX - 64 位模式:使用
RCX的变体指令(在更高级的 x86-64 扩展中有对应形式)
- 16 位模式:使用
短跳转限制
loop是“短转移”(short jump),跳转目标必须在当前指令附近(大约 -128~+127 字节范围内)。
一般普通循环体都没问题,只有特别大的函数/代码块才需要注意。循环体里不要随便改 CX/ECX
因为loop依赖CX/ECX的值来判断是否结束循环。
如果要在循环内使用ECX做别的事情,要先保存它:push ecx ; 使用别的寄存器或临时修改 ecx pop ecx多重循环
多层嵌套时,可以:- 外层用
ECX + loop - 内层用
dec/jnz、cmp/jne等普通条件跳转
或者自己用栈保存各层的计数器。
- 外层用
6. 对应到高级语言的类比
如果用 C 语言来“类比”你第二段loop的功能(忽略寄存器名),大致相当于:
inteax=0;intecx=10;do{eax++;// 循环体ecx--;// loop 做的第一步}while(ecx!=0);// loop 做的第二步:判断是否为 0,不为 0 则跳也可以理解为:
for(intecx=10;ecx>0;--ecx){eax++;}