常见的数据寻址方式
十种寻址方式,看上去要背十条公式。但它们其实只回答同一个问题:指令里那个装不下完整地址的形式地址
按”配什么”来分,十种立刻塌缩成四组:
| 配什么 | 有哪几种 |
|---|---|
| 什么都不配( | 隐含、立即、直接、寄存器 |
| 再访存一次(拿 | 间接、寄存器间接 |
| 加一个寄存器( | 相对、基址、变址 ←偏移寻址三兄弟 |
| 由 SP 隐含 | 堆栈 |
第三组三种的公式一模一样,是本节全部难点所在,本页会用一整节把它们拆开。
机制
十种寻址方式总表
| 寻址方式 | 有效地址 EA | 取操作数访存次数 | 特点 |
|---|---|---|---|
| 隐含寻址 | 隐含在操作码中 | 0 或 1 | 缩短指令字长(如 ACC) |
| 立即寻址 | 无—— | 0 | 最快,但数的范围受 |
| 直接寻址 | 1 | 简单,但寻址范围只有 | |
| 间接寻址 | 2(多级则 | 寻址范围大、便于子程序返回;慢 | |
| 寄存器寻址 | 0 | 快、指令短 | |
| 寄存器间接寻址 | 1 | 比间接寻址少一次访存 | |
| 相对寻址 | 1 | 程序浮动、段内转移 | |
| 基址寻址 | 1 | 程序重定位、多道程序 | |
| 变址寻址 | 1 | 数组、循环 | |
| 堆栈寻址 | 0(硬堆栈)/ 1(软堆栈) | 后进先出 |
立即寻址的
访存次数模板
一道题要么问”共访存几次”,要么问”取操作数访存几次”,两者差一个取指,读题时先圈出来。
四步走:
- 取指几次:指令字长 ÷ 存储字长(4.1.2)
- 每个源操作数几次:查上表,间接寻址每多一级多一次
- 结果要不要写回主存:写回寄存器/ACC 是 0 次,写回主存是 1 次(且写回地址若也是间接寻址,要再加)
- 加起来
例 单字长指令、存储字长等于指令字长。
ADD @A是一地址读改写指令,采用一次间接寻址,结果存回原处。 取指 1 + 取 EA 1 + 取操作数 1 + 存结果 1 = 4 次。 若改成直接寻址,则 1+1+1 = 3 次。
间接寻址:慢,但换来两样别的方式给不了的东西
- 突破形式地址的位数限制:
只有 8 位,但 指向的那个主存单元有 32 位——一个 8 位字段间接寻遍 4 GB。 - 地址可以在运行时改:指令本身是只读的,但
指向的那个单元可以随时改内容。这是子程序返回、跳转表、函数指针的实现基础,也是switch语句编译成跳转表时用的方式(见 4.3.2)。
多级间接需要在被指向的字上留一位标志,说明”这还是个地址,请继续”。所以一个 32 位字里往往只有 31 位是地址。这一条偶尔考。
偏移寻址三兄弟:公式相同,差别在”哪一半是变量”
三者的 EA 都是 某寄存器内容 + 形式地址
| 相对寻址 | 基址寻址 | 变址寻址 | |
|---|---|---|---|
| 基准寄存器 | PC | 基址寄存器 BR | 变址寄存器 IX |
| 寄存器内容由谁定 | 硬件自动(取指时更新) | 操作系统 / 装入程序 | 用户程序 |
| 执行期间寄存器变吗 | 随取指自动变 | 不变 | 不断变 |
| 执行期间 | 不变(编码在指令里) | 可变(不同指令给不同的 | 不变 |
| 谁是基准,谁是偏移 | PC 基准, | BR 基准, | |
| 面向 | 转移、程序浮动 | 面向系统:重定位、多道程序 | 面向用户:数组、字符串、循环 |
一句话判据(能记这一句就够):
基址寻址里,寄存器是常量、形式地址是变量;变址寻址里,形式地址是常量、寄存器是变量。
展开看就明白为什么:
- 基址:常量是”程序被装在主存哪里”(装入时定一次,运行中不变),变量是”要访问程序内部的哪个位置”(每条指令的
不同)。 - 变址:常量是”数组在哪里”(首地址,编译时定),变量是”访问第几个元素”(循环里 IX 每轮
)。
注意变址寻址里
基址与变址的复合
真实机器几乎总是把两者一起提供:
那个 int arr[] 用 double arr[] 用 arr[i] 一步到位,不必先把 LEA 那里再用一次。
堆栈寻址
PUSH/POP 自动修改 SP(多数机器的栈向低地址生长,PUSH 减 SP)。
两种实现,访存次数不同:
- 硬堆栈:用一组专用寄存器做栈,不访存,但深度有限
- 软堆栈:在主存里划一段,每次压弹都要访存一次,深度只受内存限制
“零地址指令不访存”这个说法只对硬堆栈成立,这是 4.1.2 已经提过的陷阱。
PC 相对寻址:基准是”已经加过的 PC”
这里的
例 某机按字节编址,指令长 2 字节。地址
2000H处有一条相对转移指令,位移量。转移目标是多少? 取指后 ——不是 2006H。
位移量
疑问点:偏移寻址三种的边界分别是什么,变址算是一种"思想"吗,随便找个寄存器就行吧
判据是”哪一半是变量”,上面那张表和那句判据就是完整答案。而”随便找个寄存器就行”这个判断——在真实 ISA 里完全正确,在 408 的分类语境里不成立,因为两边分的根本不是同一件事。
408 的分法 真实 ISA 的分法 依据 语义:谁维护这个寄存器、谁在变 硬件:这个位置能不能填寄存器号 有没有专用寄存器 概念上有 BR、IX x86 的 [base+index*s+disp]里 base 和 index 几乎可填任意通用寄存器;MIPS 只有offset(base)一种,压根不分基址变址结论 基址 ≠ 变址 同一套加法电路 所以”变址是一种思想”这个判断是对的——它确实是用途分类而非硬件分类。同一条
MOV AX,[BX+SI],把程序基址放 BX、下标放 SI 就是”基址+变址”;两个都放下标,硬件照算不误。硬件不知道也不关心谁是”基址”。但考场上必须按 408 的语义分类答:问”哪种寻址方式便于程序重定位”答基址,问”哪种便于数组访问”答变址,问”两者硬件实现有何不同”答没有不同,都是寄存器内容加形式地址。
疑问点:基址寄存器是抽象层的,它的具体实现就是那些段寄存器和指针
“是抽象层”这半句对;“实现是段寄存器”这半句错——段寄存器不在同一层,它在下一层。
用 4.2.1 的地址翻译链定位一下就清楚了:
第 1 层 寻址方式 BX + SI + 8 ──► EA(= 偏移地址 = 逻辑地址) 第 2 层 分段机制 DS × 16 + EA ──► 物理地址(8086 实模式)拿
MOV AX, [BX+SI+8]逐个归位:
8086 里的东西 在哪一层 对应 408 的什么 BX(Base) 第 1 层 基址寄存器 BR SI(Source Index) 第 1 层 变址寄存器 IX 8第 1 层 形式地址 DS(Data Segment) 第 2 层 不对应任何寻址方式 8086 里 408 的”基址寄存器”是有直接对应物的,就是 BX 和 BP——名字里的 B 就是 Base;SI/DI 里的 I 就是 Index。 段寄存器是另一回事:它属于地址空间的划分与保护,和寻址方式是正交的两层。
所以”8086 最后都归到了段地址 + 偏移地址”这句完全正确,但要补一句:那个”偏移地址”就是第 1 层算出来的 EA。它是寻址方式的输出、分段机制的输入,不是寻址方式的替代品。
换个角度:MIPS 根本没有段寄存器,但照样有基址寻址(
lw $t0, 8($s0))。第 2 层可以整个不存在,第 1 层照常工作——这本身就证明了两者不是同一层的东西。
疑问点:相对寻址有利于程序浮动,这不是由基址寄存器决定的吗?我记得汇编时还有一个地址转换的过程
三句话都对,但它们说的是三种不同的机制,区别在于”什么时候生效”和”指令里的地址字段要不要被改写”。
机制 谁做 何时生效 指令里的地址字段改吗 解决什么范围的浮动 静态重定位 链接器 / 装入程序 装入时,一次性 ✅ 逐条改写(要重定位表) 整个程序 基址寻址(动态重定位) OS 设 BR,硬件每次加 每次访存 ❌ 只改 BR 整个程序 PC 相对寻址 硬件 每次执行 ❌ 位移量本来就是相对的 只管段内转移 记忆里”汇编后有一个地址转换的过程”指的是第一种——静态重定位。它确实存在,而且是三者里唯一真的去修改指令字节的。把它和后两种混成一件事,正是这个疑问的来源。
至于”相对寻址有利于浮动”和”基址寄存器有利于重定位”这两句,不冲突,因为管的范围不同:相对寻址管代码内部的相互引用(跳到本模块的某个标号),基址寻址管整个程序相对于主存的位置。一个程序两者都要用。
疑问点:写汇编时定义一个 L1 然后
jmp L1,L1 只是相对地址吧,那这里不是相对寻址?前半句对,后半句反了——
jmp L1在 x86 上恰恰就是相对寻址,而且是最典型的一例。关键在于:源码里的
L1是一个符号,不是地址。 它最终变成什么,取决于汇编器为这条jmp选择了哪种机器指令编码:
你写的 汇编器选的编码 机器码里存的是 用的寻址方式 jmp L1(同段,近)EB disp8/E9 disp32位移量 下 条 指 令 地 址 PC 相对寻址 jmp far ptr L1EA ptr16:32段:偏移的绝对值 直接寻址 jmp [table+bx]FF /4从主存取目标地址 存储器间接寻址 jmp bxFF /4从寄存器取目标地址 寄存器寻址 所以”是不是相对寻址”要看汇编器最终生成的机器指令,源码写法看不出来。 而 x86 的近转移默认就编码成相对形式,这就是为什么在实践里”
jmp L1是相对寻址”几乎总是成立的。这也解释了那个”L1 只是相对地址”的直觉从哪来:汇编器算的确实是差值——
disp = L1的地址 − 下一条指令的地址——而这个差值不随程序整体搬家而改变,所以整段代码平移之后,一个字节都不用动。这正是相对寻址”利于浮动”的全部含义。
8086 常用寄存器速查
统考不考 8086 的具体寄存器,但它是”哪个寄存器属于哪一层”最好的实例,也是上面几条辨析的落脚点。
| 分组 | 寄存器 | 全称 | 用途 | 在地址翻译链的哪一层 |
|---|---|---|---|---|
| 通用数据 | AX | Accumulator | 累加器,算术运算、I/O | —— |
BX | Base | 可作基址 | 第 1 层:基址寄存器 | |
CX | Count | 循环计数(LOOP)、串操作长度 | —— | |
DX | Data | 乘除法高位、I/O 端口号 | —— | |
| 指针 | SP | Stack Pointer | 栈顶指针 | 堆栈寻址 |
BP | Base Pointer | 栈帧基址,访问参数与局部变量 | 第 1 层:基址寄存器 | |
| 变址 | SI | Source Index | 源变址、串操作源 | 第 1 层:变址寄存器 |
DI | Destination Index | 目的变址、串操作目的 | 第 1 层:变址寄存器 | |
| 段 | CS/DS/SS/ES | Code/Data/Stack/Extra Segment | 段基址 | 第 2 层:分段机制 |
| 控制 | IP | Instruction Pointer | 即 PC | 指令寻址 |
FLAGS | 标志寄存器,即 PSW | 条件转移 |
这张表的价值全在最后一列:BX/BP 与 SI/DI 在第 1 层,DS/SS 在第 2 层。名字里的 Base 和 Index 直接对应 408 的基址和变址,而段寄存器一个都不对应。
边界
“寻址方式”寻的是操作数的地址,不是操作数。 立即寻址是唯一的例外——它根本没有 EA,
相对寻址的基准是下一条指令的地址。 全章最高频的计算陷阱,例题算过一遍就不会再错。若题目特意说明”本机 PC 在执行阶段才更新”,则按题目口径来——题设永远优先于惯例。
间接寻址不等于寄存器间接寻址。 前者
基址寻址的 BR 可以是专用寄存器,也可以是某个通用寄存器。 若是通用寄存器,指令里就得给出它的编号,指令会长一点;若是专用寄存器,则隐含使用,指令短但不灵活。两种设计都有真实机器采用,题目会说明是哪种。
变址寻址与间接寻址可以复合,且顺序不同结果不同。
- 前变址(先变址后间址):
- 后变址(先间址后变址):
两者不等价,前者的变址结果是个地址的地址,后者的
堆栈寻址的”栈”向哪边长,题目必须给出。 多数机器向低地址生长(PUSH 时 PUSH 是”先减后写”还是”先写后减”。
寻址方式的种类数受寻址特征字段位数限制。
对照速查
| 寻址方式 | EA | 取操作数访存 | 一句话记忆 |
|---|---|---|---|
| 隐含 | 操作码隐含 | 0~1 | 省地址字段 |
| 立即 | 无 EA | 0 | |
| 直接 | 1 | 范围 | |
| 间接 | 2 | 范围大、可运行时改 | |
| 寄存器 | 0 | 最快 | |
| 寄存器间接 | 1 | 比间接省一次 | |
| 相对 | 1 | PC 是已增量的 | |
| 基址 | 1 | 寄存器常、 | |
| 变址 | 1 | ||
| 堆栈 | 栈顶 | 0 硬 / 1 软 | SP 隐含 |
| 判断 | 对错 |
|---|---|
| 立即寻址的有效地址等于 | ❌(没有 EA) |
| 立即数的范围是 | ❌(补码, |
| 间接寻址访存 1 次 | ❌(2 次) |
| 相对寻址的基准是当前指令地址 | ❌(下一条指令地址) |
| 基址寻址中形式地址不变 | ❌(可变,变的就是它) |
| 变址寻址中变址寄存器存数组首地址 | ❌(反了,首地址在 |
| 基址与变址的硬件实现不同 | ❌(同一套加法) |
| 段寄存器是基址寄存器的具体实现 | ❌(不在同一层) |
jmp L1(近转移)不是相对寻址 | ❌(正是相对寻址) |
| 前变址与后变址等价 | ❌ |
考点
- 十种寻址方式的 EA 公式 + 访存次数,必须能默写
- 总访存 = 取指 + 取操作数 + 存结果,间接寻址每多一级多一次
- 立即寻址没有 EA,立即数是补码
- 相对寻址的基准是已增量的 PC(下一条指令地址),位移量是补码,范围
- 偏移寻址三兄弟公式相同:判据是「基址:寄存器常、
变;变址: 常、寄存器变」 - 基址面向系统(重定位),变址面向用户(数组),硬件实现相同
- 段寄存器属于分段机制(第 2 层),不是基址寄存器(第 1 层)的实现
- 实现程序浮动有三种机制:静态重定位(改指令)、基址寻址(改 BR)、相对寻址(位移天然相对)
- 前变址
与后变址 不等价 - 寻址特征
位 → 最多 种寻址方式
链接
- 🏠 返回总览:计算机组成原理第 4 章:指令系统总览
- ⬅️ 上一节:4.2.1 指令寻址和数据寻址
- ➡️ 下一节:4.3.1 常用汇编指令介绍
- 🔗 4.3.4 过程调用的机器级表示(BP 与栈帧、堆栈寻址的实战)
- 🔗 3.6.2 页式虚拟存储器(地址翻译链的第 3~4 层)
- 🔗 OS 3.1.4 基本分段存储管理(分段机制的另一侧)
- 🔗 OS 3.1.1 内存管理基础(静态重定位与动态重定位)
- 📖 名词库:第 4 章名词库
- 📝 原始提问:#27 / #28 / #29 / #30 / #31(本地资料)