计算机组组成原理 | 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

相关推荐
茯苓gao1 天前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
HERR_QQ1 天前
强化学习的数学原理 学习笔记
人工智能·笔记·学习·自动驾驶
MartinYeung51 天前
[论文学习]DP-Fusion:面向大语言模型的令牌级差分隐私推理-深度解析
人工智能·学习·语言模型
zwenqiyu1 天前
非线性字符串数据结构串讲
数据结构·c++·学习·算法
MartinYeung51 天前
[论文学习]SecureGate:通过令牌级门控学习何时安全地揭示PII-深度解析
学习·安全
大鱼>1 天前
超参数调优进阶:Optuna/Bayesian/Early Stopping
人工智能·学习·机器学习·聚类
liuyicenysabel1 天前
多服务上线日记二:
服务器·笔记·学习
小弥儿1 天前
GitHub今日热榜 | 2026-07-05:阿里巴巴浏览器Agent上榜
学习·开源·github
‿hhh1 天前
Dify核心模块详解:从文本生成到智能体
人工智能·学习·microsoft·agent·上下文·记忆
辰海Coding1 天前
MiniSpring框架学习笔记-动态代理:如何在运行时插入逻辑?
java·笔记·学习·spring·动态代理