计算机组组成原理 | AT&T格式 和 Intel格式

🤯 同一个CPU,两套"黑话":AT&T格式 vs Intel格式,傻傻分不清楚?

寄存器前面到底要不要加"%"?操作数到底谁左谁右?一文终结你的所有困惑!

如果你曾经试图读懂Linux内核里的汇编代码,或者无意间用GCC编译时加了-S选项生成了汇编文件,你可能会对着屏幕发出灵魂拷问:

"这写的都是啥???"

明明CPU都是x86架构,凭什么我上课学的mov ax, bx到了Linux底下就变成了movl %ebx, %eax?寄存器前面多了个"%",操作数顺序还反了,读起来像在照镜子------这到底是谁发明的"阴间语法"?

今天这篇文章,就是来帮你终结这场混乱 的。我们将彻底讲透AT&T格式Intel格式这对"孪生冤家"的全部区别,让你以后无论在Windows下写汇编,还是在Linux下读内核源码,都能无缝切换、游刃有余。

🧬 第一节:为什么会有两套语法?一段不得不说的"恩怨情仇"

故事要从上世纪讲起。

Intel公司 是x86架构处理器的缔造者。1978年6月8日,大名鼎鼎的8086处理器诞生。为了让程序员不用对着二进制0101秃头,Intel顺手设计了一套汇编语言助记符------这就是Intel风格汇编的由来。Windows和DOS世界几乎被它统治。

而在另一边,AT&T公司 的前身------贝尔实验室,是C语言和Unix系统的出生地。这里的牛人们追求的是跨平台 ------他们希望汇编语言的语法 能在不同架构的CPU上尽量统一(注意是语法跨平台,不是指令跨平台)。于是他们抛开Intel的规范,自立门户搞了一套汇编语法,这就是AT&T风格汇编的起源。

当Unix被移植到i386处理器时,自然也就沿用了AT&T的汇编格式。Linux作为Unix家族的一员,同样继承了这一传统。

所以现状就是:

  • Windows阵营 → Intel格式(你上课学的那套)

  • Linux/Unix阵营 → AT&T格式(GCC默认生成的那套)

两种格式背后是两种不同的工具链生态 ,不存在谁对谁错,只是"方言"不同罢了。而考研408统考要求掌握的是Intel格式,但万一题目给的是AT&T格式呢?看懂它,就是你比别人多一分的底气。

📊 第二节:一张表终结所有混乱(核心干货)

废话不多说,直接上表。这张表就是你以后查这两种格式区别的终极词典

对比维度 AT&T 格式 Intel 格式
操作数顺序 op 源, 目的 (源在左,目的在右) op 目的, 源 (目的在左,源在右)
寄存器表示 %eax (寄存器名前必须加"%") eax (直接写寄存器名)
立即数表示 $985 (立即数前必须加"$") 985 (直接写数字)
内存地址表示 (af996h) (用小括号) [af996h] (用中括号)
数据长度标识 指令后缀:b(字节)、w(字)、l(双字) byte ptrword ptrdword ptr 前缀
简单偏移 -8(%ebx) (偏移量在括号外) [ebx - 8] (偏移量在括号内)
复杂寻址 4(%ebx, %ecx, 32) (偏移量(基址, 变址, 比例因子)) [ebx + ecx*32 + 4]基址 + 变址×比例因子 + 偏移量
指令大小写 只能用小写字母 大小写不敏感
远跳转/远调用 ljump $section, $offset jmp far section:offset
绝对跳转/调用 操作数前加 * 不需要

💡 记忆口诀 :AT&T = A lot of T rouble & T rouble(麻烦多多)------要加%、要加$、顺序还反着来。Intel = 你上课学的那套,怎么顺手怎么来。

🔍 第三节:逐条拆解,包教包会

1️⃣ 操作数顺序------最让人崩溃的区别

这是两种格式最核心、最反直觉的区别。

Intel格式mov eax, ebx → 把ebx的值赋给eax(目的在左,源在右)

AT&T格式movl %ebx, %eax → 同样是把ebx的值赋给eax(源在左,目的在右)

你看,同样一条指令,两种写法操作数位置完全相反。如果你用Intel的思维去读AT&T代码,会直接把赋值方向搞反------这是新手最常见的错误。

AT&T之所以采用"源, 目的"的顺序,是为了与之前的Unix汇编器保持兼容。而这个顺序的根源,甚至可以追溯到PDP-11处理器的指令编码方式。历史遗留问题,没办法。

快速记忆 :AT&T是" 右"读(源→目的),Intel是"左**读"(目的←源)。

2️⃣ 前缀符号------寄存器要"%",立即数要"$"

AT&T格式像个仪式感很强的人:

  • 寄存器前面必须加 %%eax%ebx

  • 立即数前面必须加 $$985$0xffff

Intel格式则简单粗暴:

  • 寄存器直接写:eaxebx

  • 立即数直接写:985、`0ffffh

举个例子,同样是"把985赋给eax寄存器":

  • AT&T:movl $985, %eax

  • Intel:mov eax, 985

快速记忆 :AT&T = "有$有%",Intel = "啥也没有"。

3️⃣ 内存地址的括号------圆括号 vs 方括号

AT&T格式 用圆括号 ( ) 表示内存地址:

  • movl (%eax), %ebx → 把eax指向的内存地址中的值赋给ebx

Intel格式 用方括号 [ ] 表示内存地址:

  • mov ebx, [eax] → 同样的意思

快速记忆 :AT&T = " 润",Intel = "正"。

4️⃣ 数据长度------后缀 vs 前缀

AT&T格式在指令后面加一个字母表示数据长度:

  • b = byte(字节,8位)

  • w = word(字,16位)

  • l = long(双字,32位)

  • q = quad word(四字,64位)

例如:movbmovwmovlmovq

Intel格式则在内存操作数前面加前缀:

  • byte ptrword ptrdword ptrqword ptr

例如:mov byte ptr [af996h], 5

快速记忆 :AT&T是"后缀 党",Intel是"前缀党"。

5️⃣ 寻址方式------从简单到复杂的完全对照

这是最容易把人绕晕的部分,我们来逐层拆解。

(1)最简单:寄存器间接寻址

AT&T Intel
movl (%ebx), %eax mov eax, [ebx]

就是把ebx当成指针,去读它指向的内存。

(2)带偏移量

AT&T Intel
movl -8(%ebx), %eax mov eax, [ebx - 8]

ebx指向的地址再减8,然后读内存。

(3)带变址和比例因子(完整形态)

这是数组访问的底层实现。假设你要访问a[i],其中a的基址在ebxiecx,每个元素占4字节:

AT&T Intel
movl 6(%ebx, %ecx, 4), %eax mov eax, [ebx + ecx*4 + 6]

完整公式:

  • AT&Tdisp(%base, %index, scale) → 地址 = disp + base + index × scale

  • Intel[base + index × scale + disp]

注意 :在AT&T中,当scale/disp出现在复杂寻址中时, 需要在它们前面加$前缀。

🧠 第四节:为什么考研要考这个?------因为真的会考!

408统考大纲在"程序的机器级代码表示"章节中,虽然没有明确指定必须用哪种格式,但历年统考真题主要考查的是Intel格式

但是!如果你只认识Intel格式,万一题目给了一段AT&T格式的代码让你分析(比如结合Linux内核源码的题目),你就直接傻眼了。两种格式的互译能力,是你在考场上比别人多拿几分的关键。

此外,GCC默认生成的汇编代码是AT&T格式的。如果你在Linux下做实验、读源码、甚至做CTF逆向题,遇到AT&T格式是家常便饭。

📚 第五节:真题闯关!看看你能对几道

🎯 真题一(格式识别题):

以下是一段汇编代码:

复制代码
movl $10, %eax
addl %ebx, %eax

请问这是哪种汇编格式?

A. Intel格式  B. AT&T格式  C. 两种都是  D. 两种都不是

【参考答案】 B

【解析】 寄存器前有%%eax%ebx),立即数前有$$10),指令用小写且有l后缀表示双字------这些都是AT&T格式的典型特征。如果是Intel格式,应该写成:

复制代码
mov eax, 10
add eax, ebx

🎯 真题二(格式转换题):

将以下Intel格式的汇编指令转换为AT&T格式:

Intel格式: mov [ebx + ecx*4 + 8], eax

【参考答案】 movl %eax, 8(%ebx, %ecx, 4)

【解析】 转换要点:

  1. 操作数顺序反转:目的[ebx+ecx*4+8]移到右边,源eax移到左边并加%

  2. 寄存器加%%eax%ebx%ecx

  3. 内存地址用圆括号:8(%ebx, %ecx, 4)

  4. 指令加l后缀表示双字:movl

🎯 真题三(AT&T代码阅读理解):

已知以下AT&T格式的汇编代码片段:

复制代码
movl 8(%ebp), %eax
movl 12(%ebp), %edx
addl %edx, %eax

请写出对应的C语言代码(假设%ebp指向当前函数的栈帧底部)。

【参考答案】

复制代码
int a, b;
a = *(int *)(ebp + 8);   // 第一个参数
b = *(int *)(ebp + 12);  // 第二个参数
return a + b;            // addl %edx, %eax,结果在%eax中

更简洁地,这对应一个简单的加法函数:

复制代码
int add(int a, int b) {
    return a + b;
}

【解析】 在x86的函数调用约定中,ebp+8通常是第一个参数,ebp+12是第二个参数。movl 8(%ebp), %eax把第一个参数读入eaxmovl 12(%ebp), %edx把第二个参数读入edxaddl %edx, %eax执行加法,结果留在eax中作为返回值。

🎯 真题四(寻址方式计算题):

AT&T格式指令:movl 0x10(%ebx, %ecx, 4), %eax

已知:%ebx = 0x1000%ecx = 0x5

请问该指令访问的内存地址是多少?

A. 0x1010  B. 0x1020  C. 0x1024  D. 0x1014

【参考答案】 C

【解析】 AT&T格式中disp(%base, %index, scale)的地址计算公式为:disp + base + index × scale

代入数值:0x10 + 0x1000 + 0x5 × 4 = 0x10 + 0x1000 + 0x14 = 0x1024

所以答案是 C

相关推荐
guslegend4 小时前
理论学习:什么是 Coding Agent?
学习
自传.4 小时前
尚硅谷 Vibe Coding|第三章(1) Claude Code深度使用与进阶技巧 学习笔记
笔记·学习·尚硅谷·vibecoding
踏着七彩祥云的小丑4 小时前
Go学习第9天:并发编程 + 文件操作 + 正则表达式
学习·golang·正则表达式·go
有Li4 小时前
PTCMIL:基于提示 token 聚类的全切片图像多实例学习分析文献速递/多模态医学影像最新进展
论文阅读·学习·数据挖掘·聚类·文献·医学生
憧憬成为web高手5 小时前
l33t-hoster
学习·web安全·网络安全
Dick5075 小时前
ROS2 常用命令表
人工智能·学习·算法·机器人
qeen875 小时前
【Linux】Linux简单介绍与基本指令(上)
linux·运维·服务器·学习
.千余5 小时前
【C++】模板进阶全解:非类型参数|全特化|偏特化|分离编译完全指南
开发语言·c++·笔记·学习·其他
自传.5 小时前
尚硅谷 Vibe Coding|第二章 AI编程工具生态 学习笔记
笔记·学习·ai编程·尚硅谷·vibe coding
库奇噜啦呼6 小时前
【iOS】RunLoop学习
学习·ios