基础元素
move eax,5;将5传输至eax保存
;寄存器与寄存器之间
;寄存器与立即数
;寄存数与存储器
;存储器与立即数
add eax,6;操作数1+操作数2
;加法指令整数常量
0A3h 十六进制 42q 八进制 26d 十进制 01010101b 二进制
有符号数
最高位为符号位,“0”代表正数,“1”代表负数
正数:
0x0000001A
0000000000000000000 0001 1010
负数:
0x1000001A
100000000000000000000011010
无符号数
所有二进制位都用于表示数值的绝对大小,没有符号位(最高位也是数据位)。 因此,无符号数只能表示 0 和正整数,永远不会是负数。
在相同的位数下,无符号数能表示的最大正数值,是有符号数最大正数值的两倍多(因为把符号位也拿来算数值了)。
最小值 (十进制 0):
0x00000000
00000000 00000000 00000000 00000000
最大值 (十进制 4,294,967,295):
0xFFFFFFFF
11111111 11111111 11111111 11111111
运算
布尔运算
布尔-小学数学老师,非常简单,true(真)和false(假)做逻辑判断,对应二进制可以表示为true为1,false为0;

与运算
两个位数都是1时,结果才为1
或运算
只要有一个为1就是1
异或运算
不一样的就是1
非运算
0就是1,1就是0
移位运算
左移
各二进位全部左移若干位,高位丢弃,低位补0

右移
各二进位全部右移若干位,低位丢弃,高位补0或者补符号位

操作数的类型
立即数
立即数就是写在指令里的常数。还是用mov操作举例子,mov 12, %rax,那么这个 12 就在操作语句里。那么 12 相当于指令里的立即数。
寄存器操作数
参加运算的数存放在指令给出的寄存器中,可以是16位或8位。例如“mov AX 8080H”就是要将8080H拷贝至寄存器AX中,但是AX在这里是数据存放目标地址的代名词。
- 通用寄存器:
EAX,EBX,ECX,EDX(用于常规数据运算与暂存)。 - 指针/变址寄存器:
ESP,EBP,ESI,EDI(用于栈操作与内存寻址)。 - 标志寄存器: **
EFLAGS - 指令指针寄存器:
EIP(存储下一条将要执行的指令地址)。
通用寄存器

EAX寄存器,EAX的低16位AX寄存器,AX的高8位AH寄存器和低8位AL寄存器。 EBX寄存器,BX寄存器,BH寄存器,BL寄存器。 ECX寄存器,CX寄存器,CH寄存器,CL寄存器。 EDX寄存器,DX寄存器,DH寄存器,DL寄存器。 ESI寄存器,ESI的低16位SI寄存器。 EDI寄存器,EDI的低16位DI寄存器。 EBP寄存器,EBP的低16位BP寄存器。 ESP寄存器,ESP的低16位SP寄存器。
标志寄存器

| 标志分类 | 简称 | 全称 | 中文名称 | 位置 | 触发条件 (置 1 的情况) |
|---|---|---|---|---|---|
| 状态 | CF | Carry Flag | 进位标志 | Bit 0 | 最高位产生进位或借位。 |
| 状态 | PF | Parity Flag | 奇偶标志 | Bit 2 | 结果的最低有效字节 (低 8 位) 中 1 的个数为偶数。 |
| 状态 | AF | Auxiliary Flag | 辅助进位标志 | Bit 4 | 第 3 位向第 4 位(半字节交界处)产生进位或借位。 |
| 状态 | ZF | Zero Flag | 零标志 | Bit 6 | 运算结果为 0。 |
| 状态 | SF | Sign Flag | 符号标志 | Bit 7 | 结果为负数(即结果的最高有效位为 1)。 |
| 控制 | TF | Trap Flag | 单步/陷阱标志 | Bit 8 | 用于程序调试,CPU 进入单步执行模式。 |
| 控制 | IF | Interrupt Flag | 中断使能标志 | Bit 9 | 允许 CPU 响应可屏蔽外部硬件中断。 |
| 控制 | DF | Direction Flag | 方向标志 | Bit 10 | 串操作指令方向:0 为地址递增,1 为地址递减。 |
| 状态 | OF | Overflow Flag | 溢出标志 | Bit 11 | 有符号数运算结果超出了可表示范围。 |
溢出标志寄存器 OF(Overflow Flag)
溢出标志 ,用于表示算术运算是否产生溢出。
溢出标志OF用于反映有符号数加减运算所得结果是否溢出。如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0。
无符号,进位看CF 有符号,进位看OF 有无符号是自定义的
最高位进位与溢出的区别:
- 进位标志表示无符号数运算结果是否超出范围.
- 溢出标志表示有符号数运算结果是否超出范围.
溢出主要是给有符号运算使用的,在有符号的运算中,有如下的规律:
- 正 + 正 = 正 如果结果是负数,则说明有溢出
- 负 + 负 = 负 如果结果是正数,则说明有溢出
- 正 + 负 永远都不会有溢出
方向标志寄存器 DF(Direction Flag)
方向标志 ,控制串操作指令的方向。
中止使能标志寄存器IF(Interrupt Enable Flag)
中断使能标志 ,用于控制可屏蔽中断的使能与禁止。
单步标志寄存器 TF(Trap Flag)
单步标志 ,用于设置单步调试模式。
符号标志寄存器 SF(Sign Flag)
符号标志 ,反映算术运算结果的符号。
零标志寄存器 ZF(Zero Flag)
零标志 ,表示运算结果是否为零。
如果运算结果为0,则其值为1,否则其值为0
辅助进位标志寄存器 AF(Auxiliary Carry Flag)
辅助进位标志 ,主要用于 BCD 码算术运算中辅助进位检测。
它关注低四位在运算时产生的进位或借位
奇偶标志寄存器 PF(Parity Flag)
奇偶标志 ,反映运算结果中 “1” 的个数的奇偶性。
如果”1”的个数是偶数,则PF的值为1,否则值为0
进位标志寄存器 CF(Carry Flag)
如果运算结果的最高位产生了一个进位或者借位,那么,其值为1,否则为0 含义: 运算结果是否超出了无符号数的表示范围。
mov AL,0xEF
add AL,2
内存操作数
内存操作数(Memory Operand)是汇编指令中指向计算机主存(RAM)地址的数据引用
在汇编中是[]括号内的计算结果,它是一个内存地址,去该地址读取或写入数据,而不是直接使用这个数值本身。
硬指令
汇编传输指令
mov指令
MOV指令,能实现以下操作:
CPU内部寄存器之间数据的任意传送(除了码段寄存器CS和指令指针IP以外)。
立即数传送至CPU内部的通用寄存器组(即AX、BX、CX、DX、BP、SP、SI、DI),给这些寄存器赋初值。
CPU内部寄存器(除了CS和IP以外)与存储器(所有寻址方式)之间的数据传送,可以实现一个字节或一个字的传送。
能实现用立即数给存储单元赋初值。
语法
mov r/m8,r8
mov r/m116,r16
mov r/m32,r32
mov r8,r/m8
mov r16,r/m16
mov r32,r/m32
mov r8,imm8
mov r16,imm16
mov r32,,imm32
说明:
- r 通用寄存器
- m 内存
- imm 立即数
- r8 8位通用寄存器
- m8 8位内存
- imm8 8位立即数
注意:
- 源操作数可以是立即数、通用寄存器、段寄存器、或者内存单元
- 目标操作数可以是通用寄存器、段寄存器、或者内存单元
- 操作数的宽度必须一样
- 源操作数和目标操作数不能同时为内存单元
扩展指令
movzx(Move with Zero-Extend - 零扩展):- 机制: 将源数据放入目标寄存器的低位,目标寄存器剩余的高位全部硬性填充为 0。
- C 语言映射: 无符号类型之间的向上转换。例如
unsigned char转换为unsigned int或int。
movsx(Move with Sign-Extend - 符号扩展):- 机制: 将源数据放入目标寄存器的低位,目标寄存器剩余的高位根据源数据的最高位(符号位)进行填充。如果源数据最高位是 1(负数),高位全填 1;如果是 0(正数),高位全填 0,以此保证扩展前后数值的符号和大小不变。
- C 语言映射: 有符号类型之间的向上转换。例如
char转换为int。
LAHF指令
用于将标志寄存器的低八位送入AH,即将标志寄存器FLAGS中的SF、ZF、AF、PF、CF五个标志位分别传送到累加器AH的对应位,八位中有三位是无效的;
SAHF指令
SAHF(保存 AH 内容到状态标志位)指令将 AH 内容复制到 EFLAGS(或 RFLAGS)寄存器低字节。例如,可以检索之前保存到变量中的标志位数值:
XCHG指令
XCHG 指令用于交换两个操作数的内容,它是 mov 指令的简化版。XCHG 指令的操作数格式与 mov 指令相同,但不能使用立即数作为操作数。
算术运算指令
add指令
ADD指令的基本格式是_ADD DST, SRC_,其中DST是目标操作数,SRC是源操作数。DST和SRC不能同时为存储器,DST不能为立即数。例如,以下是一些使用ADD指令的示例:
ADD AX, BX ; 将AX和BX寄存器的值相加,结果存储在AX中
ADD AL, 10 ; 将AL寄存器的值与立即数10相加,结果存储在AL中
sub指令
直接减数字:
SUB AL, 5 → 把AL寄存器里的值减5(8位)
SUB AX, 100 → 把AX寄存器里的值减100(16位)
SUB EAX, 1000→ 把EAX寄存器里的值减1000(32位)
变量/内存减数字:
SUB [num], 10 → 把内存里叫num的变量减10
可以减8位、16位或32位的数字
寄存器互减:
SUB AX, BX → AX = AX - BX
SUB CL, DL → CL = CL - DL
也可以反过来:SUB EAX, [num] → 用EAX减内存里的值
inc指令
指令用于将寄存器或内存中的操作数加 1,而不影响进位标志(CF)
dec指令
DEC指令用于将操作数减1,同时不影响进位标志CF,但会更新其他状态标志位。
逻辑运算指令
and指令
核心定义: AND 是逻辑与指令。对两个操作数按位进行与运算,结果存入目标操作数。 运算规则: 对应的位都为 1 时结果才为 1,只要有 0 结果就是 0。 常见核心用途:掩码操作(提取或清零特定位) 常用于保留操作数的某些特定位,同时将其他无关的位强制清零。 示例:
AND EAX, 0x000000FF → 将 EAX 的高 24 位清零,只保留低 8 位(相当于提取低8位的值)
AND AL, 11111110b → 将 AL 寄存器的最低位强制清零,其他位保持不变
AND EBX, EBX → 寄存器的值不变,但会刷新标志位(常用于快速判断寄存器是否为0,然后接 JZ 等跳转指令)or指令
核心定义: OR 是逻辑或指令。对两个操作数按位进行或运算,结果存入目标操作数。 运算规则: 对应的位只要有一个为 1,结果就为 1;都为 0 时结果才为 0。
常见核心用途:置位操作(将特定位置 1) 常用于将操作数的某些特定位强制设置为 1,而不影响其他的位。
示例:
OR EAX, 0x00000001 → 将 EAX 的最低位强制置为 1,其他位状态保持不变
OR CL, 00001000b → 将 CL 的第 3 位(从 0 开始算)强制置为 1
OR EDX, EDX → 寄存器的值不变,刷新标志位(判断是否为0,效果同 AND EDX, EDX)
xor指令
核心定义: XOR(Exclusive OR)是逻辑异或指令。对两个操作数按位进行异或运算,结果存入目标操作数。 运算规则: 对应的位相同为 0,不同为 1。 常见核心用途:快速清零寄存器 在汇编中,通常使用 XOR 寄存器, 寄存器 来清零,而不是用 MOV 寄存器, 0。因为 XOR 占用字节更少,执行速度通常更快。
示例:
XOR EAX, EAX → 将 EAX 寄存器清零 (最常见用法,等同于 EAX = 0) 2
XOR AL, 0x0F → 将 AL 与立即数按位异或 3
XOR EAX, EBX → 将 EAX 与 EBX 异或,结果存入 EAX (EAX = EAX ^ EBX)not指令
核心定义: NOT 是逻辑按位取反指令。这是一个单操作数指令,对操作数中的每一位进行反转(0 变 1,1 变 0),结果存回原处。
示例:
NOT EAX → 将 EAX 寄存器中的值按位取反
NOT byte ptr [num] → 将内存变量按位取反
注:NOT 指令比较特殊,它是唯一一个不影响任何状态标志位(如 CF, ZF, OF 等均不受影响)的逻辑运算指令。
跳转指令
jmp指令
核心定义
jmp(Jump)指令是 x86 汇编中最基础的无条件转移指令。 它的核心作用是强制改变 CPU 的执行流。在微观物理层面上,执行 jmp 指令本质上就是通过硬件电路强制修改 CPU 内部的指令指针寄存器(IP/EIP/RIP),有时还会连同**代码段寄存器(CS)**一起修改,从而让 CPU 去新的内存地址取指执行,而不再按顺序执行下一条紧邻的指令。
底层执行原理
- 流水线刷新 (Pipeline Flush): 现代 CPU 具有指令预取和流水线(Pipeline)机制。当译码器遇到
jmp指令时,CPU 意识到即将发生跳转,此前预取到流水线中的后续顺序指令将全部作废(Flush),这在微体系结构中被称为“分支惩罚(Branch Penalty)”。 - 目标地址计算: CPU 的地址生成单元(AGU)根据指令中携带的操作数(相对偏移量或绝对地址),计算出真实的目标物理/虚拟地址。
- 寄存器覆写: 将计算出的目标地址覆写到
IP(段内转移)或CS:IP(段间转移)寄存器中。 - 重新取指: CPU 的总线接口单元(BIU)根据新的
CS:IP指向的地址,重新向主存发起取指请求,程序流完成跳转。
寻址机制与转移类型
jmp 指令根据修改的寄存器范围和跳转距离,严格划分为以下两大类、三种具体形态:
段内转移
原理: 目标代码与当前 jmp 指令处于同一个代码段内。因此,只需要修改 IP 寄存器的值,CS(代码段寄存器)保持不变。 根据 IP 修改的跨度(位移量),又细分为:
- 段内短转移
- 语法:
jmp short 标号 - 机器码特征: 操作码
EB+ 8位相对位移量 (disp8)。 - 位移范围:
128 ~ 127字节。 - 地址计算公式:
新 IP = 当前 IP + 8位位移量(注意:此处的“当前 IP”是指 CPU 已经读取完这句jmp指令后,指向的下一条指令的首地址)。位移量以补码形式存放,最高位为符号位,决定向前(负跳)还是向后(正跳)。
- 语法:
- 段内近转移
- 语法:
jmp near ptr 标号 - 机器码特征: 操作码
E9+ 16位或32位相对位移量 (disp16或disp32)。 - 位移范围: * 在 16 位实模式下:
-32768 ~ 32767字节。- 在 32 位保护模式下:可以覆盖整个 4GB 线性地址空间。
- 地址计算公式:
新 IP = 当前 IP + 16/32位位移量
- 语法:
段间转移
原理: 目标代码与当前 jmp 指令不在同一个代码段。这就要求必须同时修改 CS(代码段寄存器)和 IP(指令指针寄存器)。
-
段间远转移 (Far Jump)
- 语法:
jmp far ptr 标号 - 机制: 标号不仅包含目标地址的偏移量,还隐式包含了目标地址所在的段基址。
- 地址执行细节: CPU 会用标号所在段的段地址覆盖当前的
CS寄存器,用标号在段内的偏移地址覆盖当前的IP寄存器。 - 注:在 32/64 位操作系统的保护模式下,Far Jump 的机制变得更为复杂,通常涉及段描述符表(GDT/LDT)、特权级检查(如通过调用门 Call Gate 实现 Ring 3 到 Ring 0 的切换),而不仅是简单的数值替换。
- 语法:
JCC指令
| JCC指令 | 中文含义 | 英文原意 | 检查符号位 | 典型C应用 |
|---|---|---|---|---|
| JZ/JE | 若为0则跳转;若相等则跳转 | jump if zero;jump if equal | ZF=1 | if (i j);if (i 0); |
| JNZ/JNE | 若不为0则跳转;若不相等则跳转 | jump if not zero;jump if not equal | ZF=0 | if (i != j);if (i != 0); |
| JS | 若为负则跳转 | jump if sign | SF=1 | if (i < 0); |
| JNS | 若为正则跳转 | jump if not sign | SF=0 | if (i > 0); |
| JP/JPE | 若1出现次数为偶数则跳转 | jump if Parity (Even) | PF=1 | (null) |
| JNP/JPO | 若1出现次数为奇数则跳转 | jump if not parity (odd) | PF=0 | (null) |
| JO | 若溢出则跳转 | jump if overflow | OF=1 | (null) |
| JNO | 若无溢出则跳转 | jump if not overflow | OF=0 | (null) |
| JC/JB/JNAE | 若进位则跳转;若低于则跳转;若不高于等于则跳转 | jump if carry;jump if below;jump if not above equal | CF=1 | if (i < j); |
| JNC/JNB/JAE | 若无进位则跳转;若不低于则跳转;若高于等于则跳转; | jump if not carry;jump if not below;jump if above equal | CF=0 | if (i >= j); |
| JBE/JNA | 若低于等于则跳转;若不高于则跳转 | jump if below equal;jump if not above | ZF=1或CF=1 | if (i ⇐ j); |
| JNBE/JA | 若不低于等于则跳转;若高于则跳转 | jump if not below equal;jump if above | ZF=0或CF=0 | if (i > j); |
| JL/JNGE | 若小于则跳转;若不大于等于则跳转 | jump if less;jump if not greater equal | SF != OF | if (si < sj); |
| JNL/JGE | 若不小于则跳转;若大于等于则跳转; | jump if not less;jump if greater equal | SF = OF | if (si >= sj); |
| JLE/JNG | 若小于等于则跳转;若不大于则跳转 | jump if less equal;jump if not greater | ZF != OF 或 ZF=1 | if (si ⇐ sj); |
| JNLE/JG | 若不小于等于则跳转;若大于则跳转 | jump if not less equal;jump if greater | SF=OF 且 ZF=0 | if(si>sj) |
loop指令
核心定义
loop 指令是 x86 架构中专门用于控制已知循环次数的精简指令。
从汇编语法上看,它只需要一个操作数(即跳转的目标标号)。但从底层微架构的视角来看,它隐式地(Implicitly)绑定了 CPU 的计数寄存器(CX / ECX / RCX,取决于工作模式)。它将“计数器递减”和“条件跳转”这两个动作浓缩在了一个机器码周期内。
底层执行原理与微架构动作
当 CPU 的指令译码器遇到 loop 标号 指令时,会在内部将其拆解为以下微操作(Micro-operations, µops)序列:
-
隐式递减 (Implicit Decrement): CPU 的算术逻辑单元(ALU)自动将计数寄存器(在 16 位下是
CX,32 位下是ECX)的值减去 1。即执行内部等效的ECX = ECX - 1。 -
零值探测 (Zero Detection): CPU 检查递减后的计数寄存器的值是否等于 0。
-
条件分支 (Conditional Branch):
-
如果不为 0 (
ECX ≠ 0): CPU 的地址生成单元(AGU)计算标号的地址,并将其覆写进指令指针寄存器(IP/EIP),程序流跳转回循环体的开头。 -
如果为 0 (
ECX = 0): 循环结束。CPU 不做任何跳转操作,直接按顺序向下执行loop指令紧挨着的下一条指令。
-
⚠️ 核心专业细节(与 DEC + JNZ 的本质区别):
通常开发者会认为 loop label 等价于 dec ecx 加上 jnz label。在逻辑结果上确实如此,但在微观状态上有一个致命区别:
-
执行
dec ecx会**修改标志寄存器(EFLAGS)**的状态(如 ZF, SF, OF 等)。 -
执行
loop指令递减计数器时,绝不会影响任何 EFLAGS 标志位。这种设计允许在循环体内部进行复杂的带进位/借位运算(如adc,sbb),而不必担心loop指令会破坏运算产生的标志位。
指令寻址机制与物理限制
与 jmp 指令拥有多种跳转距离不同,loop 指令在物理设计上被严格限制为段内短转移 (Short Jump)。
-
机器码特征: 操作码
E2+ 8位相对位移量 (disp8)。 -
位移范围: 目标标号所在的地址,必须在当前
loop指令下一条指令地址的 -128 到 +127 字节范围内。 -
工程意义: 这种设计强迫编译器或汇编程序员将循环体控制在较小的代码块内,这非常契合 CPU 一级指令缓存(L1i Cache)的局部性原理(Locality of Reference),有助于提高缓存命中率。
LOOP 指令族扩展
除了基础的无条件计数循环,x86 指令集还提供了结合标志位状态的复杂循环指令。它们不仅检查 ECX 是否为 0,还额外检查零标志位(ZF)的状态:
| 指令语法 | 别名 | 执行条件(必须同时满足两项,否则退出循环) | 典型应用场景 |
|---|---|---|---|
loope 标号 | loopz | 1. ECX = ECX - 1 ≠ 02. ZF = 1 (上一次运算结果为零/相等) | 在数组中查找第一个不等于某值的元素。 |
loopne 标号 | loopnz | 1. ECX = ECX - 1 ≠ 02. ZF = 0 (上一次运算结果非零/不相等) | 在字符串或数组中查找第一个等于某特定字符/数值的元素。 |
现代微体系结构视角的性能考量
虽然 loop 指令在汇编代码的视觉上非常简洁优雅(用一条指令代替了两条指令),但在现代超标量(Superscalar)和具备复杂分支预测(Branch Prediction)的 CPU 上(如 Intel Core 架构、AMD Zen 架构):
-
loop指令的执行往往比拆分开的dec ecx配合jnz label更慢。 -
原因: 现代 CPU 的解码器更擅长将简单的 RISC 风格指令(如
dec和jnz)进行宏指令融合(Macro-op Fusion)并高效塞入乱序执行(Out-of-Order Execution)流水线。而像loop这样带有复杂历史包袱的复合 CISC 指令,反而需要被分解成微代码(Microcode)序列执行,消耗更多时钟周期。 -
编译器行为: 因此,现代的 C/C++ 编译器(如 GCC, Clang, MSVC)在开启优化(-O2, -O3)后,几乎绝不会生成
loop指令,而是清一色地使用dec/sub配合条件跳转指令来实现for或while循环。
伪指令
offset指令
它的核心作用是在汇编阶段获取变量或标号(Label)在其所在段内的相对偏移地址。
在 x86 架构的内存寻址模型中,物理地址的定位通常由“段基址(Segment Base)”和“偏移地址(Offset Address)”两部分共同完成:
- 段基址:定义了内存段的起始物理位置。
- 偏移地址:定义了具体数据或指令相对于该段首地址的线性距离。
offset 操作符的作用正是提取这一段内距离。当汇编器(Assembler)扫描源代码并遇到 offset 标识符 时,它会计算出该标识符相对于其所在数据段或代码段起始位置的字节偏移量。随后,汇编器会将这个计算出的数值作为一个常量立即数,直接硬编码到最终生成的机器码指令中。
ptr指令
为什么需要 ptr?(消除宽度歧义)
当 CPU 访问内存时,它必须同时明确两个物理要素:
- 基址(Base Address):从内存的哪个位置开始读写。
- 长度(Size/Width):连续读写多少个字节。
在很多指令中,汇编器可以通过寄存器的宽度来隐式推断内存操作的长度。例如: mov eax, [ebx] 因为 eax 是一个 32 位(4 字节)寄存器,汇编器会自动知道需要从 ebx 指向的内存地址处连续读取 4 个字节。此时不需要 ptr。
但在某些场景下,汇编器会陷入 “宽度歧义” ,最典型的就是将立即数(常量)直接写入内存: mov [ebx], 1 这条指令在编译阶段会报错。因为汇编器不知道这个 1 应该作为 1 个字节(01)、2 个字节(00 01)还是 4 个字节(00 00 00 01)写入目标内存。这时就必须通过 ptr 强制干预。
标准的 ptr 宽度规范
在逆向分析和底层开发中,你会频繁接触到以下几种标准的宽度定义符:
BYTE PTR:按 8 位(1 字节)操作。WORD PTR:按 16 位(2 字节)操作。DWORD PTR:按 32 位(4 字节)操作(Double Word)。QWORD PTR:按 64 位(8 字节)操作(Quad Word,在 x64 架构下常用)。
type指令
在 MASM/TASM 汇编体系中,type 与之前提到的 offset 和 ptr 一样,都是伪操作符(Pseudo-operator)。它在汇编(编译)阶段由汇编器进行静态求值,本身绝不会生成任何直接对应的机器码指令。
1. 核心定义与作用
type 操作符的核心作用是:返回变量或标号(Label)在定义时所占据的物理字节数(Byte Size),或者返回代码标号的调用类型标识值。
你可以将其等价理解为 C/C++ 语言中针对基本数据类型的 sizeof() 运算符在汇编层的静态映射。
2. 标准返回值对照
当汇编器在源代码中扫描到 type 加上一个已定义的变量名时,会根据该变量的数据宽度,将其在编译期直接替换为一个常量立即数:
db(Define Byte) 返回值:1dw(Define Word) 返回值:2dd(Define Double Word) 返回值:4dq(Define Quad Word) 返回值:8
lengthof指令
sizeof指令
寻址方式
在计算机体系结构和汇编语言中,寻址方式(Addressing Modes) 是指微处理器在执行指令时寻找操作数(Operand)物理地址的机制。理解不同的寻址方式是掌握底层内存管理、数据结构访问以及指令执行效率的关键。
立即寻址方式
概念定义:
在立即寻址中,操作数本身作为指令的一部分直接编码在机器码中,紧跟在操作码(Opcode)之后。它主要用于为寄存器或内存位置赋初值。
代码解析:
mov eax, 11 ; 立即数 11 直接存入 eax
add eax, 114504 ; 立即数 114504 与 eax 的内容相加
sub eax, 1 ; 立即数 1 从 eax 的内容中扣除
专业特点:
- 执行效率极高: CPU在取指阶段(Instruction Fetch)就直接获取了操作数,无需额外的内存读取周期(Memory Read Cycle)。
- 局限性: 只能作为源操作数(Source Operand),不能作为目的操作数(Destination Operand,因为立即数没有内存地址可供写入)。
寄存器寻址方式
概念定义:
指令指定的寄存器中存放着所需的操作数。CPU 直接在内部寄存器堆(Register File)中进行读写操作。
代码解析:
mov ebx, 0x36d ; 源操作数为立即寻址,目的操作数为寄存器寻址
mov edx, ebx ; 源和目的皆为寄存器寻址:将 ebx 的内容复制到 edx
专业特点:
- 速度最快: 寄存器位于 CPU 内部,访问延迟在所有存储层级中最低。
- 资源受限: CPU 内的通用寄存器(GPRs)数量有限(如 x86 架构下的 EAX, EBX, ECX, EDX 等),编译器需要通过寄存器分配算法(Register Allocation)进行优化调度。
直接寻址方式
概念定义:
指令中直接包含了操作数所在的内存有效地址(Effective Address, EA)。通常表现为一个变量名或标号(Label)。
代码解析:
mov ecx, msg ; 将标号 msg 所代表的内存绝对地址(或偏移地址)装入 ecx
注:在某些汇编器(如 NASM)中,msg 表示地址本身;而在 MASM 中,通常使用 OFFSET msg 获取地址,直接写 msg 可能会被解析为读取该地址的内容 [msg]。此处代码的逻辑是获取地址,为后续的指针操作做准备。
专业特点:
- 确定性强: 内存地址在编译或链接阶段就已经固定(硬编码)。
- 适用场景: 常用于访问全局变量或静态数据区。
寄存器间接寻址方式
概念定义:
操作数的有效地址存放在某个特定的通用寄存器中。CPU 首先读取寄存器的内容,将其作为指针(Pointer),然后去访问该指针指向的物理内存。
代码解析:
mov esi, msg ; 将内存地址存入变址寄存器 esi
mov eax, [esi] ; 间接寻址:读取 esi 内部存储的地址所对应的内存数据,存入 eax
专业特点:
- 动态灵活性: 允许在程序运行期间动态修改寄存器中的地址,是实现指针操作、字符串遍历的核心机制。
寄存器相对寻址方式
概念定义:
操作数的有效地址是基址寄存器或变址寄存器的内容,加上指令中指定的一个常数偏移量(Displacement)。
代码解析:
mov ecx, msg ; 基址准备
add ecx, 4 ; 基址偏移
mov eax, [ecx] ; 此时也可以直接写成 mov eax, [msg + 4]
; 或者是 mov eax, [ecx + 4] (假设 ecx 初始为 msg)
专业特点:
- 数据结构访问: 这是访问结构体(Struct)成员或类对象的标准模式。寄存器指向结构体的首地址,偏移量对应结构体内特定成员的字节位置。
基址变址寻址方式
概念定义:
有效地址由一个基址寄存器(Base Register)的值,加上一个变址寄存器(Index Register)的值乘以比例因子(Scale Factor,通常为 1、2、4 或 8)组合而成。
代码解析:
mov ecx, msg ; ecx 作为基址寄存器
mov edx, 2 ; edx 作为变址寄存器,表示索引值
mov eax, [ecx + edx*2] ; EA = ecx + edx * 2。将计算出的地址对应的数据读入 eax
专业特点:
- 数组遍历的利器: 极其适合访问一维数组。
ecx存储数组首地址,edx作为数组下标(Index),*2表示数组元素的大小为 2 字节(例如 16-bit 的 Word 类型)。
相对基址变址寻址方式
概念定义:
这是 x86 架构中最复杂的寻址方式,结合了前面几种模式。有效地址等于:基址寄存器 + (变址寄存器 × 比例因子) + 常数偏移量。
代码解析:
mov ecx, msg ; 基址
mov edx, 1 ; 索引
add ecx, 8 ; 基址预处理(增加8字节偏移)
mov eax, [ecx + edx*2 - 6] ; 综合计算 EA = ecx(此时为msg+8) + (1 * 2) - 6
专业特点:
- 高阶数据结构映射: 专门用于处理复杂的数据结构,例如二维数组、包含数组的结构体、或者栈帧(Stack Frame)中局部变量与参数的复杂定位。这种寻址方式利用了 CPU 内部的地址计算单元(AGU, Address Generation Unit),能在单条指令周期内完成复杂的指针算术运算,极大地优化了执行效率。