想象你是一位仓库管理员,面前有一排排整齐的储物柜。你的日常工作就是三件事:把货物从一处搬运到另一处、对货物的数量做加减运算、以及检查两个柜子里的货物是否相同。这套流程看似简单,却是整个仓库高效运转的核心。在计算机的世界里,CPU 就是那位勤奋的仓库管理员,而 MOV、ADD、CMP 就是它最得心应手的三大工具。MOV 负责搬运数据,ADD 负责算术运算,CMP 则负责比较判断。理解这三个基础指令如何协同工作,你就掌握了理解整个计算机运行逻辑的钥匙。
掌握 x86 基础指令是理解 CPU 执行模型的基石,对从事编译器开发、逆向工程、性能优化等 AI 基础设施工作的从业者而言,这是必经之路。
可视化演示
展示MOV数据传送流程、ADD与CMP标志位设置、JE条件跳转分支执行三阶段交互探索。
本节配有交互动效(进入视口后自动加载,约 3KB)。
MOV数据传送指令 源操作数复制到目的操作数 支持寄存器到寄存器传送 不改变源操作数值 MOV dst, src MOV是x86最基本的数据传送指令
寄存器与寻址
仓库管理员的第一课,是认识仓库的格局。CPU 内部那些小巧而高速的寄存器,就像是管理员工作台上随手可及的便签本;而内存则像是远处巨大的货架区。每次从便签本上读取信息只需一瞬,但跑到货架区翻找就要慢得多。这便签本上的每一页,在 x86 世界里都有一个名字:AX、BX、CX、DX,以及专用的栈指针 SP、指令指针 IP 等等。
寻址方式,就是告诉 CPU “到哪儿去取货物” 的路径描述。最简单的方式,是把数字直接写进指令里,这叫立即数寻址——好比管理员在便签上直接写 “5“,不用去任何地方取货。直接寻址则像是给出 ”3 号货架第 2 格” 这样的门牌号,CPU 按地址去内存中取数。而寄存器寻址最为常用,因为数据就在手边的便签本里,取用最快。
在 x86 汇编中,一条指令通常写成 操作码 目标, 源 的形式。操作码就是动作命令(MOV、ADD、CMP),逗号左边是目的地,右边是来源。这种 “从左到右” 的阅读方式,与自然语言中 “把 A 给 B” 的习惯恰好相反,初学者极易搞混。
就像仓库管理员需要先熟悉便签本和货架的布局才能高效工作,理解寄存器与寻址方式,是掌握所有 x86 指令的第一步。
- 寻址方式集合寻址方式是 CPU 获取操作数(即参与运算的数据)的途径,不同方式开销不同、灵活性不同。
- 立即数寻址操作数直接编码在指令中,无需额外访问内存,执行速度最快,但数值是写死的。
- 寄存器寻址操作数存放在寄存器中,执行速度快,且值可以动态改变,是最常用的寻址方式。
MOV数据搬运
MOV 指令是 CPU 世界里的“搬运工”。它就像管理员把货物从一个便签移到另一个便签、或从便签移到货架指定位置。注意,MOV 本质是“复制”而非“剪切”——源位置的数据不会消失,正如你用复印机复制文件,原件依然完好。
MOV 的语法非常直观:MOV 目标, 源。目标通常是寄存器或内存位置,源可以是立即数、寄存器或内存。但 x86 规定,两个操作数不能同时来自内存——这就像管理员不能直接从货架 A 搬到货架 B,而必须先把货物搬到手边的工作台(寄存器)上,再放到货架 B。
这里的 [1000H] 表示内存地址,加方括号是告诉 CPU “按这个地址去存取”,而不是把这个数字本身当作数据。这正是指令中 “RS“ 语义的关键:方括号是”解引用”的标志。
MOV 就像是仓库管理员的搬运准则:从哪儿拿到哪儿、什么能直接搬、什么必须经手工作台。掌握了它,你就理解了数据流动的基本法则。
- 目标赋值箭头方向指示数据流动方向,目标位置收到源位置的值的副本。
- 源不动等号右端的源值在操作后仍然保留,这是复制而非移动。
- 寻址约束目标与源不能同时为内存操作数,必须通过寄存器中转。
MOV AX, 5 ; 立即数 5 送入 AX(在便签本写数字 5)MOV BX, AX ; AX 的值复制到 BX(便签内容抄一份到另一页)MOV [1000H], AX ; AX 的值存入地址 1000H 的内存单元(放入货架指定格)
ADD算术运算
如果说 MOV 是搬运工,那 ADD 就是计算器。ADD 指令执行加法运算,并把结果存回目标位置——这好比管理员在便签上写下 “当前数量 + 新增数量 = 最新数量”,然后直接把便签上的旧数字擦掉,改成新数字。
ADD 的语法与 MOV 完全一致:ADD 目标, 源,效果是把目标与源相加,结果写回目标。这条指令会自动更新标志寄存器中的相关标志位,尤其是进位标志 CF 和溢出标志 OF。标志位就像是便签旁边的“状态指示灯”,告诉你刚才的计算有没有产生特殊情况。
注意最后一个例子:0x3000 + 0xE000 = 0x11000,超过了 16 位寄存器能表示的最大值 0xFFFF,于是高位的 1 溢出到进位标志 CF。同时,如果从有符号数角度看,结果还可能触发溢出标志 OF。
ADD 的运行规律就像管理员的心算:不仅算出结果,还会留意“灯”的变化。这些标志位看似不起眼,却是后续 CMP 与条件跳转的基础。
- 加法核心操作目标寄存器的值与被加的源值做二进制加法。
- 零标志 ZF如果结果为 0,则 ZF = 1,表示结果是“零”;否则为 0。
- 进位标志 CF用于无符号数运算,最高位产生进位时 CF = 1。
- 溢出标志 OF用于有符号数运算,当结果超出有符号数表示范围时 OF = 1。
MOV AX, 1000H ; AX = 0x1000(便签写上初始数量 4096)ADD AX, 2000H ; AX = 0x3000(加上 8192,得 12288)ADD AX, 0E000H ; AX = 0x1000,CF=1(超过 16 位上限,进位灯亮起)
CMP比较判断
CMP 指令是 CPU 的“比较器”。它执行一次减法操作 A - B,但不保存相减的结果——只更新标志位。就像管理员把两个便签上的数字默默对比一下,然后只在心里记下“谁大谁小”的结论,而不在便签上留下任何运算痕迹。
CMP 的比较结果体现在三个标志位上。如果 A 等于 B,相减结果为 0,零标志 ZF = 1;如果 A 小于 B(有符号视角),符号标志 SF 会反映结果的符号;如果 A 小于 B(无符号视角),则会产生借位,进位标志 CF = 1。
CMP 通常不单独使用,而是与条件跳转指令(如 JE、JNE、JG、JL)配合。条件跳转指令读取这些标志位,决定程序的下一步走向。这正是指令中 "RS" 语义的延伸:通过标志位间接地传递比较结果。
CMP 就如同天平上的一次快速称量——不移动任何货物,只在刻度盘上留下指针位置。接下来读表(标志位)的人,就是条件跳转指令。
- 减法操作在 CPU 内部执行 A - B 的运算,但结果不写回任何寄存器。
- 零判断若结果为 0,则 ZF 置 1,说明 A 等于 B。
- 借位判断无符号视角下,若 A < B 则产生借位,CF 置 1。
- 符号判断有符号视角下,结果最高位为 1 时 SF 置 1,表示结果为负。
MOV AX, 10 ; AX = 10MOV BX, 20 ; BX = 20CMP AX, BX ; 计算 10 - 20 = -10,不保存结果 ; SF = 1(结果为负),CF = 1(无符号借位),ZF = 0
JMP流程控制
程序默认是顺序执行的,就像管理员按照清单逐项处理任务。但有时需要根据条件跳转,比如“如果库存不足,就去补货”。JMP 指令正是这条“跳转路径”的载体。
无条件跳转 JMP label 直接改变程序执行流,跳转到指定标签处继续执行——好比管理员直接翻到手册第 10 页而不管前面的内容。更有用的是条件跳转:JE(相等则跳)、JNE(不等则跳)、JG(大于则跳)、JL(小于则跳)等,它们配合 CMP 实现分支逻辑。
这段代码展示了分支结构:CMP 设置标志位,JE 根据 ZF 决定是否跳转。如果 AX 等于 BX(ZF = 1),程序跳到 equal 标签处执行 MOV CX, 20;否则顺序执行 MOV CX, 10。循环结构则通过条件跳转回指令序列的开头实现。
JMP 与条件跳转让程序拥有了“决策能力”。仓库管理员不再只是机械地照单执行,而能根据库存状态(标志位)灵活调整行动路线。
- 条件判断JE 指令首先检查零标志 ZF 的值,决定是否跳转。
- PC 更新若条件成立,则程序计数器 PC 跳转到目标标签的地址。
- 顺序不成立若条件不成立,PC 继续指向下一条指令,程序正常往下执行。
MOV AX, 5MOV BX, 5CMP AX, BX ; 比较 AX 与 BXJE equal ; 若相等则跳转到 equalMOV CX, 10 ; 不等时执行JMP doneequal: MOV CX, 20 ; 相等时执行done: ...
指令组合实战
单独的指令就像单字,组合起来才成为语言。真实程序正是由成千上万条 MOV、ADD、CMP、JMP 组合而成。以最常见的 “变量交换” 为例,不能直接用一条指令交换两个寄存器的值——那就像让两个人同时坐到对方椅子上一样不可能。必须引入第三个临时寄存器暂存。
再看循环累加的例子,它组合了 MOV、ADD、CMP、JMP 四种指令:
这段代码计算 0+1+2+……+9 的和。MOV 负责初始化,ADD 负责任务递增和计数,CMP 与 JNE 配合形成循环出口。四个指令各司其职,组合出完整的循环逻辑。
从交换数据到循环累加,四个基础指令的组合就像管理员的一套标准操作流程:搬运、计算、比较、决策。掌握了这些组合模式,你就能读懂任何汇编程序。
- 初始化阶段MOV 指令为循环变量和累加器设置初值。
- 累加操作ADD 指令将当前计数器的值加到累加器。
- 计数递增计数器在每次循环末尾加 1,确保循环能推进。
- 边界判断CMP 与 JNE 配合,当计数器不等于 10 时跳回循环起点。
; 交换 AX 与 BX 的值MOV CX, AX ; CX = AX(把 A 的值暂存到临时位)MOV AX, BX ; AX = BX(B 坐到了 A 的位置)MOV BX, CX ; BX = 原 AX(临时值放到 B 的位置)
MOV CX, 0 ; 计数器清零MOV AX, 0 ; 累加器清零loop_start:ADD AX, CX ; 累加ADD CX, 1 ; 计数器加 1CMP CX, 10 ; 比较是否达到 10JNE loop_start ; 未达到则继续循环
AI 应用场景
编译器后端指令生成
阶段:代码生成/优化
解决的问题:编译器后端需要把高层语言翻译成目标机器的汇编指令。如果没有对 MOV、ADD、CMP 等指令的深刻理解,编译器生成的代码会冗长低效。AI 辅助编译器通过模式匹配和成本模型,选择最优的指令序列来完成同样的逻辑。
具体案例:Clang/LLVM 的后端在生成 x86 代码时,会将 a = b + c 这样的中间表示翻译为 MOV 加载变量到寄存器、ADD 执行加法、再 MOV 存回内存的指令序列。AI 优化模块会分析标志位使用情况,比如发现 CMP 之后紧跟的条件跳转可以合并为一条带条件码的指令,从而减少指令数。
程序分析中的指令语义识别
阶段:特征提取/模式识别
解决的问题:安全分析中,恶意代码检测需要理解程序的行为逻辑。传统特征码匹配无法应对变种恶意代码,而基于 AI 的程序分析通过识别 MOV/ADD/CMP 组成的“语义模式”(如解密循环、反调试检测)来判定程序类别。
具体案例:在 objdump 工具反汇编出的指令流中,AI 模型识别出连续多条 ADD 和 CMP 指令构成的模式,判断这是一个嵌入式勒索软件的加密密钥生成逻辑。通过将这些模式作为特征输入分类器,能够在新变种出现时快速捕获。
程序语义嵌入与检索
阶段:语义嵌入/比对
解决的问题:跨架构的二进制代码相似性检测是软件供应链安全的核心挑战。AI 模型需要理解指令的语义(如“把值加载到寄存器并比较”),而不仅仅是文本层面的操作码。
具体案例:训练一个基于指令嵌入的 AI 模型时,将每条汇编指令看作“操作码+操作数类型”的组合。模型通过对比学习,让 MOV AX, BX 与 MOV ECX, EDX 在语义空间中相近(都是寄存器复制),而 ADD AX, 1 则与它们距离较远。这样在跨编译器、跨优化等级的代码匹配中,准确率大幅提升。
Python 演示
#!/usr/bin/env python3"""Simple x86 instruction simulator for MOV/ADD/CMP/JMP.""" class Simulator: def __init__(self): self.regs = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0} self.flags = {'ZF': 0, 'SF': 0, 'CF': 0, 'OF': 0} self.memory = {} self.pc = 0 self.pc_count = 0 def get_value(self, op): if op in self.regs: return self.regs[op] if op.startswith('[') and op.endswith(']'): addr = int(op[1:-1], 16) return self.memory.get(addr, 0) return int(op, 16) def set_value(self, target, value): if target in self.regs: self.regs[target] = value & 0xFFFF elif target.startswith('[') and target.endswith(']'): addr = int(target[1:-1], 16) self.memory[addr] = value & 0xFFFF def update_flags(self, result): self.flags['ZF'] = 1 if result == 0 else 0 self.flags['SF'] = 1 if (result & 0x8000) else 0 self.flags['CF'] = 1 if result > 0xFFFF else 0 self.flags['OF'] = 1 if result > 0x7FFF or result < -0x8000 else 0 def run(self, instructions): self.pc_count = 0 labels = {} for i, ins in enumerate(instructions): if ins.endswith(':'): labels[ins[:-1]] = i while self.pc < len(instructions): ins = instructions[self.pc] self.pc_count += 1 parts = ins.replace(',', ' ').split() if not parts: self.pc += 1 continue op = parts[0].upper() if op == 'MOV': self.set_value(parts[1], self.get_value(parts[2])) elif op == 'ADD': dst, src = parts[1], self.get_value(parts[2]) result = self.get_value(dst) + src self.set_value(dst, result) self.update_flags(result) elif op == 'CMP': a, b = self.get_value(parts[1]), self.get_value(parts[2]) self.update_flags(a - b) elif op == 'JE' and self.flags['ZF'] == 1: self.pc = labels[parts[1]] continue elif op == 'JNE' and self.flags['ZF'] == 0: self.pc = labels[parts[1]] continue elif op == 'JMP': self.pc = labels[parts[1]] continue self.pc += 1 return self.regs, self.flags, self.pc_count def main(): sim = Simulator() program = [ 'MOV AX, 0', 'MOV CX, 0', 'loop_start:', 'ADD AX, CX', 'ADD CX, 1', 'CMP CX, 10', 'JNE loop_start' ] regs, flags, steps = sim.run(program) print('Final registers:', regs) print('Final flags:', flags) print('Total steps:', steps) assert regs['AX'] == 45, 'Expected 45 (sum of 0..9)' print('PASS: sum 0+1+...+9 =', regs['AX']) if __name__ == '__main__': main()
class Simulator:定义模拟器类,封装寄存器、标志位、内存和程序计数器。self.regs = {'AX': 0, ...}用字典模拟 16 位寄存器组,属性加_后缀避免与方法同名。def get_value(self, op):解析操作数:寄存器查表、内存解引用、数字直接转换。self.flags['ZF'] = 1 if result == 0 else 0根据运算结果更新零标志位。if ins.endswith(':'):预处理阶段扫描标签,记录跳转目标地址。op == 'MOV'执行数据传送,调用set_value写回目标位置。self.update_flags(a - b)CMP 的核心:做减法但不保存结果,只更新标志位。elif op == 'JE' and self.flags['ZF'] == 1:条件跳转:检查零标志并决定是否更新 PC。assert regs['AX'] == 45最终结果断言,确保循环累加逻辑的正确性。
常见误区
误区我总觉得 MOV AX, BX 会把 BX 里的数据“搬走”,就像把文件从 A 文件夹拖到 B 文件夹后 A 文件夹就空了,所以执行完后 BX 应该变成 0。
正解MOV 实际上是复制操作而非移动操作。执行 MOV AX, BX 后,BX 的值保持不变,只是 AX 被更新为 BX 的副本。这就像用复印机复印文件,原件和复印件同时存在。在汇编中,没有一条指令能让寄存器之间的数据“移动”后让源寄存器清零——如果需要清空源寄存器,必须再执行一条 MOV BX, 0 或 XOR BX, BX。
为何常见:“MOV“ 这个助记符本身就带有”移动”的含义,加上高级语言中赋值语句 a = b 让初学者容易混淆:高级语言的赋值其实也是复制语义,但人们通常只关心被赋值变量的值变化,忽略了源变量的状态。
误区ADD 和 CMP 执行完,只要结果是想要的值就没问题了,那些 ZF、CF、SF 标志位似乎无关紧要,反正我读结果时用不到它们。
正解标志位是 CPU 与后续指令通信的“便签”,会在指令执行后保留。一个常见的坑是:CMP AX, BX 之后,又执行了一条 MOV CX, AX,然后才用 JE 判断——此时 ZF 已经被 MOV 指令破坏了(MOV 不更新标志位,但在某些处理器上 MOV 之后标志位保持不变,而若中间插入 ADD 等会改变标志的指令,判断就失效了)。正确的做法是让条件跳转紧跟 CMP,或在跳转前不要穿插任何会修改标志位的指令。
为何常见:初学者往往把每条指令当作孤立操作,忽略指令间的“副作用”传递。汇编程序的 bug 常常就藏在这些被忽略的标志位变化中。
误区CMP 指令比较两个数的大小,直接看结果的正负号就行。`CMP AX, BX` 如果结果是负数那就说明 AX 小于 BX。
正解CMP 本身不区分有符号和无符号,区别在于你使用哪组条件跳转指令。无符号比较用 JA(高于则跳)、JB(低于则跳);有符号比较用 JG(大于则跳)、JL(小于则跳)。关键区别在于:对于字节值 0xFF(无符号 255 / 有符号 -1),如果与 0x01 比较,无符号视角下 255 > 1,但有符号视角下 -1 < 1。错误地混用跳转指令会得到完全相反的结果。
为何常见:高级语言中 int 和 unsigned int 的比较由编译器根据类型自动选择合适的跳转指令,初学者看不到这一层,因此容易忽略底层的符号差异。
误区ADD 指令就是做加法用的,想实现其他运算得用别的指令。如果我要把 AX 乘以 2,那肯定要找 MUL 指令来完成。
正解ADD 指令可以用来实现多种操作。比如 ADD AX, AX 实现 AX 翻倍,等同于左移一位。灵活运用 ADD、SUB(减法)和移位指令,可以在没有乘除法指令的早期 CPU 上高效实现乘法运算。此外,ADD AX, 1 就是最简单的计数器递增,这是循环中最常用的操作之一。
为何常见:按“功能名”理解指令是一种直觉习惯,但计算机底层是通用计算设备——一条指令的用途远不止助记符的字面含义,关键在于理解它能对数据产生什么样的变换。
误区`MOV AX, 1000` 和 `MOV AX, [1000]` 我觉得是一样的,反正最终 AX 里的值都是 1000。
正解两者完全不同!MOV AX, 1000 是立即数寻址,直接把数值 1000 赋给 AX。而 MOV AX, [1000] 是直接寻址,它去内存地址 1000 处取出那个单元里存的值再赋给 AX。如果地址 1000 处存储的值是 42,则第一条指令后 AX = 1000,第二条指令后 AX = 42。方括号是“解引用”的符号,表示按地址访问内存。
为何常见:高级语言中没有这种显式的地址解引用语法(C 语言的 *ptr 除外),初学者对“数值”与“地址处的值”的区分缺乏直觉,需要反复练习才能根深蒂固。
面试题
Q1. 执行 MOV AX, 5 之后 MOV BX, AX,以下描述正确的是?
- A.AX = 5,BX = 0
- B.AX = 0,BX = 5
- C.AX = 5,BX = 5← 正确答案
- D.AX = 0,BX = 0
答案:C。MOV 是复制操作。第二条指令 MOV BX, AX 把 AX 当前的值 5 复制到 BX 中,AX 本身保持不变。选择 A 和 B 都包含“某寄存器变为 0”的误解,D 则是完全没有执行任何有效操作。
Q2. ADD AX, 0FFFEH 在 16 位寄存器中执行后,进位标志 CF 下列说法正确的是?
- A.只要结果大于 65535,CF 就为 1← 正确答案
- B.CF 只在结果为 0 时才为 1
- C.CF 与 ADD 指令无关
- D.CF 表示结果是否为负数
答案:A。进位标志 CF 用于无符号数运算,当加法结果超出目标寄存器能表示的最大值(16 位即 65535)时,CF 被置 1。0xFFFF + 1 的结果是 0x10000,超出 16 位范围,因此 CF = 1。B 描述的是 ZF 的行为,C 和 D 是完全错误的认知。
Q3. 执行 CMP AX, BX 后零标志 ZF = 1,说明什么?
- A.AX > BX
- B.AX = BX← 正确答案
- C.AX < BX
- D.无法确定
答案:B。CMP 执行的是 AX - BX 的减法运算。当结果为 0 时,ZF 置 1。结果为 0 意味着 AX - BX = 0,即 AX = BX。选项 A 对应结果为正且 ZF = 0,选项 C 对应结果为负,D 不正确因为 ZF = 1 已经给出了明确的相等信息。
Q4. 以下哪条指令会导致程序计数器 PC 无条件跳转到指定标签?
- A.CMP label
- B.JE label
- C.JMP label← 正确答案
- D.MOV label
答案:C。JMP 是无条件跳转指令,执行后 PC 直接指向目标标签。CMP 是比较指令,不改变执行流。JE 是条件跳转,仅在 ZF = 1 时跳转。MOV 是数据传送指令,不能用于跳转。注意区分无条件跳转与条件跳转的使用场景。
Q5. 要实现“若 AX == BX 则跳转到 equal”,正确的指令组合是?
- A.ADD AX, BX 后 JE equal
- B.CMP AX, BX 后 JNE equal
- C.CMP AX, BX 后 JE equal← 正确答案
- D.MOV AX, BX 后 JE equal
答案:C。CMP 比较 AX 与 BX,若相等则 ZF = 1,JE 检查 ZF 并在其为 1 时跳转。选项 B 用的是 JNE(不等则跳),逻辑相反。选项 A 用 ADD 会改变 AX 的值且不能用 JE 直接判断相等。D 选项 MOV 会覆盖 AX 的值,完全错误。
Q6. 16 位寄存器中 ADD AX, 7FFFH(AX 初始为 1),下列关于 OF 的说法正确的是?
- A.OF = 0,因为结果为正数
- B.OF = 1,因为结果超出了有符号数 16 位范围← 正确答案
- C.OF 与 CF 的值一定相同
- D.OF 与有符号数无关
答案:B。1 + 0x7FFF = 0x8000,在有符号数视角下是 -32768,而两个正数(1 和 32767)相加不可能得到负数,说明产生了溢出,所以 OF = 1。注意 OF 针对有符号数,CF 针对无符号数,两者不一定相同——这是区分两个标志位的核心考点。
Q7. 关于 MOV 指令,下列说法正确的有哪些?
- A.MOV 是复制操作,源数据不会被清除← 正确答案
- B.MOV AX, BX 和 MOV BX, AX 效果完全相同
- C.两个操作数不能同时为内存操作数← 正确答案
- D.MOV AX, 5 是将数值 5 送入 AX 寄存器← 正确答案
答案:ACD。A 正确,MOV 是复制语义。C 正确,x86 架构规定 MOV 的两个操作数不能同时来自内存,必须通过寄存器中转。D 正确,立即数 5 直接赋给 AX。B 错误,两条指令的方向相反,前者把 BX 的值赋给 AX,后者把 AX 的值赋给 BX,执行后的寄存器状态完全不同。
Q8. 在执行 CMP AX, BX 后,以下哪些说法是正确的?
- A.如果 AX = BX,则 ZF = 1← 正确答案
- B.如果 AX < BX(无符号视角),则 CF = 1← 正确答案
- C.AX 和 BX 的值都会被修改
- D.如果 AX > BX(有符号视角),则 SF = 0 且 ZF = 0← 正确答案
答案:ABD。CMP 执行减法但不保存结果,C 错误,AX 和 BX 本身的值不会被修改。A 正确,相等时结果为 0,ZF = 1。B 正确,无符号视角下 AX < BX 说明发生了借位,CF = 1。D 正确,有符号视角下 AX > BX 意味着结果为正(SF = 0)且不为零(ZF = 0)。
Q9. 下列代码执行后,CX 的值是多少?
- A.0
- B.1← 正确答案
- C.2
- D.无限循环
答案:B。循环过程:第一次循环,CX 变为 1,比较 AX(1)与 CX(1),相等,ZF = 1,JNE 不跳转,循环结束,CX = 1。选项 B 正确。注意这里循环只执行了一次就退出,因为循环条件是在 CX 递增后立即生效的。
Q10. 关于 JMP 相关指令,以下说法正确的是?
- A.JMP 是无条件跳转,JE 是条件跳转← 正确答案
- B.条件跳转通常与 CMP 搭配使用← 正确答案
- C.JE 跳转的前提是 ZF = 0
- D.JL 和 JB 都可以用于有符号比较
答案:AB。A 正确,JMP 无条件改变执行流,JE 根据标志位条件跳转。B 正确,CMP 设置标志位后,条件跳转指令根据标志位决定是否跳转,这是标准的比较-分支模式。C 错误,JE 的前提是 ZF = 1。D 错误,JB(低于则跳)用于无符号比较,有符号小于用 JL,混用会导致有符号数比较结果错误。