3.4 数据传送指令

数据传送指令(mov 系列)是 x86-64 中最基础、最常用的指令,负责在寄存器与内存之间搬运数据。

mov 指令族

mov 指令有四种大小变体:

指令大小操作数宽度
movbbyte1 字节
movwword2 字节
movldouble word / long4 字节
movqquad word8 字节

基本语法

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,适用于无符号数。

指令源大小目的大小示例
movzbw1 字节2 字节movzbw %al, %ax
movzbl1 字节4 字节movzbl %al, %eax
movzbq1 字节8 字节movzbq %al, %rax
movzwl2 字节4 字节movzwl %ax, %eax
movzwq2 字节8 字节movzwq %ax, %rax
# 零扩展示例
movzbq  %al, %rax          # %rax = 0x00000000000000xx, xx 为 %al 的值

符号扩展:movs 系列

将高位填充为源操作数的符号位,适用于有符号数。

指令源大小目的大小示例
movsbw1 字节2 字节movsbw %al, %ax
movsbl1 字节4 字节movsbl %al, %eax
movsbq1 字节8 字节movsbq %al, %rax
movswq2 字节8 字节movswq %ax, %rax
movslq4 字节8 字节movslq %eax, %rax
# 符号扩展示例
# 假设 %al = 0xFF (即 -1 的 byte 表示)
movsbq  %al, %rax          # %rax = 0xFFFFFFFFFFFFFFFF (即 -1)
movzbq  %al, %rax          # %rax = 0x00000000000000FF (即 255)

cltq 系列(特殊符号扩展)

指令等效操作说明
$cbtwmovsbw %al, %ax
$cwtlmovswl %ax, %eax
cwtdmovswl %ax, %eax 的同义词
$cltqmovslq %eax, %rax
cqtdmovslq %eax, %rax 的同义词
cqto将 %rax符号扩展到 $%rdx:%raxquad → oct
# cltq 的典型用法
# C 语言:long a = (int)x;
movl    %edi, %eax         # 将 32 位参数放入 %eax
cltq                        # 符号扩展到 64 位 %rax

movabsq: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                     # 返回 %rax

mov 指令的常见用途

  1. 加载值:从内存或立即数加载到寄存器
  2. 存储值:将寄存器的值写回内存
  3. 寄存器清零movl $0, %eax(或更高效的 xorl %eax, %eax
  4. 寄存器间复制movq %rax, %rbx
  5. 类型转换:通过 movz/movs 系列实现整数类型提升

相关笔记

    • mov 指令的操作数格式
    • mov 涉及的目标寄存器
    • mov 的内存寻址模式
    • 与 mov 的区别(不访存)

链接到