刚开始学汇编,MOV指令的操作数顺序把我整懵了。Intel文档写MOV R8, 42,GCC生成的汇编却是MOV $42, %rax。一个说"目标, 源",一个说"源, 目标",到底谁对谁错?其实两种都对,只是语法不同。这篇把MOV的数据流向彻底理清楚。
两种语法,完全相反的写法
| 语法 | 格式 | 示例 | 含义 |
|---|---|---|---|
| Intel/NASM | MOV 目标, 源 |
MOV R8, 42 |
把42存进R8 |
| AT&T/GAS | MOV 源, 目标 |
MOV $42, %rax |
把42存进%rax |
核心区别 :Intel把目标放前面,AT&T把目标放后面。但数据流向是一样的------都是从源到目标。
核心
不管用哪种汇编语法,MOV指令的核心作用只有一个:把源操作数(SRC)的值复制到目标操作数(DST) ,源操作数不会被修改,只有目标会变。
唯一的区别是:不同语法里,"源"和"目标"在指令里的位置不一样。
; Intel: 目标 ← 源
MOV RAX, RBX ; RAX = RBX,RBX不变
; AT&T: 源 → 目标(写法相反)
MOV %rbx, %rax ; %rax = %rbx,%rbx不变
AT&T用%标记寄存器,$标记立即数,这是为了和C语言区分。Intel语法则更直白,直接写名字。
为什么会有两种语法
历史包袱:
- Intel语法:Intel自己定的标准,Windows环境、IDA反编译、大部分教材都用这个
- AT&T语法 :Unix传统,GCC、GDB、Linux内核源码用这个,和C语言赋值方向相反(C是
dst = src,AT&T是MOV src, dst)
实际影响:
- 看Windows教程用Intel,看Linux内核用AT&T
- 混着看容易精神分裂,建议先专精一种
常见坑
坑1:以为MOV是"移动"
MOV其实是复制,源操作数不会被清空。
MOV RAX, RBX ; 复制后RBX还在,不是剪切
坑2:AT&T的立即数和内存
MOV $42, %rax ; $42是立即数,存到rax
MOV 42, %rax ; 没有$,42是内存地址,把地址42的内容读到rax
MOV %rax, 42 ; 把rax写到内存地址42(AT&T语法)
Intel语法反过来:
MOV RAX, 42 ; 把42存到rax(立即数)
MOV RAX, [42] ; 把地址42的内容读到rax(要加方括号)
坑3:操作数大小不明确
AT&T要在指令后加后缀:
MOVB $1, %al ; byte,8位
MOVW $1, %ax ; word,16位
MOVL $1, %eax ; long,32位
MOVQ $1, %rax ; quad,64位
Intel靠寄存器名字区分,或者加BYTE PTR等修饰:
MOV AL, 1 ; 8位
MOV AX, 1 ; 16位
MOV EAX, 1 ; 32位
MOV RAX, 1 ; 64位
快速对照表
| 操作 | Intel | AT&T |
|---|---|---|
| 寄存器→寄存器 | MOV RAX, RBX |
MOV %rbx, %rax |
| 立即数→寄存器 | MOV RAX, 42 |
MOV $42, %rax |
| 内存→寄存器 | MOV RAX, [RBX] |
MOV (%rbx), %rax |
| 寄存器→内存 | MOV [RBX], RAX |
MOV %rax, (%rbx) |
| 立即数→内存 | MOV QWORD PTR [RBX], 42 |
MOVQ $42, (%rbx) |
一句话总结
MOV的数据永远从源 流向目标 。Intel语法是
MOV 目标, 源,AT&T是MOV 源, 目标,记住这个就不容易写反了。
建议新手从Intel语法入手,资料多、直观好懂。遇到AT&T代码时,心里默念"顺序反过来"就行。