x86汇编语言数据类型和整数运算

一、 数据在内存中的表示(数据类型)

在汇编层面,“数据类型”主要指数据在内存中的存储尺寸解释方式。CPU指令根据尺寸决定操作多少内存。

类型 大小 (比特/字节) 表示范围(无符号) 表示范围(有符号) 常见用途
BYTE 8 bits / 1 byte 0 到 255 (0 到 FFh) -128 到 +127 (80h ~ 7Fh) ASCII字符,小整数
WORD 16 bits / 2 bytes 0 到 65,535 (0 到 FFFFh) -32,768 到 +32,767 短整数,旧式16位数据
DWORD 32 bits / 4 bytes 0 到 4,294,967,295 -2,147,483,648 到 +2,147,483,647 整数,内存地址(32位模式)
QWORD 64 bits / 8 bytes 0 到 \(1.84 \times 10^{19}\) \(-9.22 \times 10^{18}\)\(+9.22 \times 10^{18}\) 64位整数,大型偏移

关键概念:

  • 字节序:x86架构采用小端序。例如,双字 12345678h 在内存中的存储顺序(从低地址到高地址)为:78h 56h 34h 12h
  • 有符号与无符号:内存中的二进制模式相同,解释权在指令0xFF 作为无符号 BYTE 是 255,作为有符号 BYTE 是 -1。

定义数据示例(MASM):

.data
    bVal   BYTE   255      ; 无符号字节,FFh
    sVal   SBYTE  -1       ; 有符号字节,也是FFh
    wVal   WORD   4660h    ; 字,内存中为 60h 46h
    dVal   DWORD  12345678h; 双字
    array  BYTE   1,2,3,4  ; 字节数组
    szMsg  BYTE   'Hello',0; 以0结尾的字符串

二、 核心整数运算指令与标志位

运算的核心是寄存器标志寄存器(EFLAGS)。指令会更新标志,后续条件跳转指令(如 JZ, JC)依赖这些标志。

1. 数据传送指令

指令 格式 作用 对标志位影响
MOV MOV dest, src 将源数据复制到目标
LEA LEA reg, mem 将内存操作数的有效地址加载到寄存器,不访问内存本身
XCHG XCHG op1, op2 交换两个操作数的值
MOV EAX, dVal      ; 将内存dVal处的双字复制到EAX
LEA ESI, array     ; 将array的地址存入ESI,相当于 MOV ESI, OFFSET array
XCHG EAX, EBX      ; 交换EAX和EBX的值

2. 算术运算指令

下表总结了关键指令及其对标志位的影响(*表示直接影响):

指令 格式 作用 OF(溢出) SF(符号) ZF(零) CF(进位)
ADD ADD dest, src 加法:dest = dest + src * * * *
SUB SUB dest, src 减法:dest = dest - src * * * *
INC INC op 自增:op = op + 1 * * *
DEC DEC op 自减:op = op - 1 * * *
NEG NEG op 求补(取负):op = -op(即0-op) * * * *
MUL MUL reg/mem 无符号乘法:将EAX与操作数乘,结果存EDX:EAX * ? ? *
IMUL IMUL dest, src1[, src2] 有符号乘法:灵活的单/双/三操作数形式 * ? ? *
DIV DIV reg/mem 无符号除法:EDX:EAX ÷ op,商EAX,余EDX 无定义 无定义 无定义 无定义
IDIV IDIV reg/mem 有符号除法:EDX:EAX ÷ op,商EAX,余EDX 无定义 无定义 无定义 无定义

关键图解:有符号与无符号乘除法的寄存器使用

flowchart TD A[开始乘法/除法] --> B{MUL/IMUL 还是 DIV/IDIV?}; B --> C[MUL 或 IMUL<br>(单操作数形式)]; C --> D[“操作数 × EAX”]; D --> E[“结果存于 EDX:EAX<br>(高64位:低64位)”]; B --> F[DIV 或 IDIV]; F --> G[“被除数必须预置于 EDX:EAX<br>(高64位:低64位)”]; G --> H[“操作数作为除数”]; H --> I[“商存入 EAX, 余数存入 EDX”];

运算示例:

; 加减法
MOV EAX, 10
ADD EAX, 5       ; EAX=15, CF=0, ZF=0, SF=0
SUB EAX, 20      ; EAX=-5 (FFFFFFFBh), CF=1(借位), ZF=0, SF=1

; 自增自减
INC dword ptr [wVal] ; 注意:操作数大小需明确

; 取负
MOV EBX, 8
NEG EBX          ; EBX=-8, CF=1

; 乘法
MOV EAX, 5000
MOV EBX, 1000
IMUL EBX         ; 有符号乘: EDX:EAX = 5,000,000
; 或使用双操作数形式
IMUL ECX, EBX, 200 ; ECX = EBX * 200

; 除法(务必先设置EDX)
MOV EAX, 100     ; 被除数低32位
MOV EDX, 0       ; 被除数高32位清零(对于无符号除)
MOV EBX, 9       ; 除数
DIV EBX          ; EAX=11(商), EDX=1(余数)

3. 移位与循环移位指令

这些指令高效,常用于乘除2的幂、位操作和数据提取。

指令 格式 作用图示/描述 移出的位去哪了? 典型用途
SHL/SAL SHL dest, count 逻辑/算术左移:CF ← [ ← dest ← ] ← 0 进入 CF 无符号数 × \(2^n\)
SHR SHR dest, count 逻辑右移:0 → [ → dest → ] → CF 进入 CF 无符号数 ÷ \(2^n\)
SAR SAR dest, count 算术右移:MSB → [ → dest → ] → CF 进入 CF 有符号数 ÷ \(2^n\)(保持符号)
ROL ROL dest, count 循环左移:CF ← [ ← dest ← ] ←(首尾相连) 同时进入 CF 和操作数另一端 位重组,高位与低位互换
ROR ROR dest, count 循环右移:→ dest → ] → CF →(首尾相连) 同时进入 CF 和操作数另一端 位重组,低位与高位互换
; 移位示例
MOV AL, 10110001b ; 0xB1
SHL AL, 1         ; AL=01100010b (0x62), CF=1  (乘以2,溢出)
MOV BL, 10001100b ; 0x8C (-116有符号,140无符号)
SAR BL, 2         ; BL=11100011b (0xE3, -29有符号), CF=0 (有符号除以4,向下舍入)
SHR BL, 2         ; BL=00100011b (0x23, 35无符号), CF=1 (无符号除以4)

; 循环移位示例
MOV AH, 11000011b
ROL AH, 3         ; AH=00011110b, CF=0 (将高3位循环移至低3位)

4. 比较与位测试指令

指令 格式 作用 实质
CMP CMP op1, op2 比较 op1 - op2,根据结果设置标志,不保存结果 隐式的 SUB,用于条件判断
TEST TEST op1, op2 对两个操作数做逻辑与,根据结果设置标志,不保存结果 隐式的 AND,用于测试特定位
CMP EAX, 10       ; 若EAX=10,则ZF=1;若EAX<10(无符号),则CF=1
TEST EBX, 1       ; 测试EBX最低位是否为1(奇偶),若为0则ZF=1

三、 关键编程提示与调试技巧

  • 明确操作数大小:使用 BYTE PTRWORD PTR 等修饰符避免歧义。例如 INC BYTE PTR [esi]
  • 除法前的准备:进行 DIV/IDIV 前,务必正确设置 EDX:EAX。无符号除用 XOR EDX, EDX 清零;有符号除用 CDQ 扩展。
  • 善用LEALEA 可用于快速进行整数运算(如 LEA EAX, [EBX + ECX*4 + 10]),它不访问内存,只计算地址。
  • 标志位是核心:理解 JC(有进位跳)、JZ(为零跳)、JO(溢出跳)、JS(为负跳)等条件跳转指令依赖的标志。
  • 调试观察:在调试器中(如VS或OllyDbg),单步执行并密切关注EFLAGS寄存器的变化,这是理解指令行为最直接的方法。
posted @ 2025-12-20 12:23  ffff5  阅读(0)  评论(0)    收藏  举报