3.4 数据传送指令
数据传送指令(mov 系列)是 x86-64 中最基础、最常用的指令,负责在寄存器与内存之间搬运数据。
mov 指令族
mov 指令有四种大小变体:
| 指令 | 大小 | 操作数宽度 |
|---|---|---|
movb | byte | 1 字节 |
movw | word | 2 字节 |
movl | double word / long | 4 字节 |
movq | quad word | 8 字节 |
基本语法
mov<size> 源操作数, 目的操作数将源操作数的值复制到目的操作数。源操作数不变。
合法的操作数组合
| 源 目的 | 示例 | 说明 |
|---|---|---|
| 立即数 → 寄存器 | movq $100, %rax | 最常用 |
| 立即数 → 内存 | movq $100, (%rdi) | 立即数写入内存 |
| 寄存器 寄存器 | movq %rax, %rbx | 寄存器间复制 |
| 寄存器 内存 | movq %rax, (%rdi) | 写内存 |
| 内存 寄存器 | movq (%rdi), %rax | 读内存 |
限制:x86-64 不允许 内存到内存 的直接传送。数据从内存到内存必须经过寄存器中转。
# 错误:不能直接从内存到内存
# movq (%rdi), (%rsi) ❌
# 正确:需要经过寄存器中转
movq (%rdi), %rax #内存 $→$寄存器
movq %rax, (%rsi) #寄存器 $→$内存零扩展与符号扩展
当将较小宽度的数据传送到较大宽度时,需要扩展高位。
零扩展:movz 系列
将高位填充为 0,适用于无符号数。
| 指令 | 源大小 | 目的大小 | 示例 |
|---|---|---|---|
movzbw | 1 字节 | 2 字节 | movzbw %al, %ax |
movzbl | 1 字节 | 4 字节 | movzbl %al, %eax |
movzbq | 1 字节 | 8 字节 | movzbq %al, %rax |
movzwl | 2 字节 | 4 字节 | movzwl %ax, %eax |
movzwq | 2 字节 | 8 字节 | movzwq %ax, %rax |
# 零扩展示例
movzbq %al, %rax # %rax = 0x00000000000000xx, xx 为 %al 的值符号扩展:movs 系列
将高位填充为源操作数的符号位,适用于有符号数。
| 指令 | 源大小 | 目的大小 | 示例 |
|---|---|---|---|
movsbw | 1 字节 | 2 字节 | movsbw %al, %ax |
movsbl | 1 字节 | 4 字节 | movsbl %al, %eax |
movsbq | 1 字节 | 8 字节 | movsbq %al, %rax |
movswq | 2 字节 | 8 字节 | movswq %ax, %rax |
movslq | 4 字节 | 8 字节 | movslq %eax, %rax |
# 符号扩展示例
# 假设 %al = 0xFF (即 -1 的 byte 表示)
movsbq %al, %rax # %rax = 0xFFFFFFFFFFFFFFFF (即 -1)
movzbq %al, %rax # %rax = 0x00000000000000FF (即 255)cltq 系列(特殊符号扩展)
| 指令 | 等效操作 | 说明 |
|---|---|---|
| $ | cbtw | movsbw %al, %ax |
| $ | cwtl | movswl %ax, %eax |
cwtd | movswl %ax, %eax 的同义词 | — |
| $ | cltq | movslq %eax, %rax |
cqtd | movslq %eax, %rax 的同义词 | — |
cqto | 将 %rax符号扩展到 $%rdx:%rax | quad → oct |
# cltq 的典型用法
# C 语言:long a = (int)x;
movl %edi, %eax # 将 32 位参数放入 %eax
cltq # 符号扩展到 64 位 %raxmovabsq:64 位立即数
普通的 movq 只能使用 32 位有符号立即数(符号扩展到 64 位)。要加载完整的 64 位立即数,需要使用 movabsq。
movq $0x12345678, %rax # 合法:0x12345678 在 32 位范围内
movabsq $0x123456789ABCDEF0, %rax # 合法:完整的 64 位立即数
# movq $0x123456789ABCDEF0, %rax # 非法:64 位立即数超出 movq 范围实际操作示例
// C 源代码
long exchange(long *xp, long y) {
long x = *xp;
*xp = y;
return x;
}# 编译后的汇编
exchange:
movq (%rdi), %rax # rax = *xp (读取旧值)
movq %rsi, (%rdi) # *xp = y (写入新值)
ret # 返回 %raxmov 指令的常见用途
- 加载值:从内存或立即数加载到寄存器
- 存储值:将寄存器的值写回内存
- 寄存器清零:
movl $0, %eax(或更高效的xorl %eax, %eax) - 寄存器间复制:
movq %rax, %rbx - 类型转换:通过 movz/movs 系列实现整数类型提升
相关笔记
-
- mov 指令的操作数格式
-
- mov 涉及的目标寄存器
-
- mov 的内存寻址模式
-
- 与 mov 的区别(不访存)