购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.6 寻址模式

在x86的Intel语法中,内存地址由方括号表示。例如,[0x1234]表示程序应访问位于0x1234地址的内存。

然而,内存寻址并不仅限于使用如0x1234这样的立即数来指定地址。x86支持几种不同的寻址模式。这些寻址模式被用来访问不同类型的变量。

2.6.1 绝对寻址

绝对寻址采用固定值来指定地址。这个固定值可以以任何进制来确定(如[1]或[0x1234])。也可以用某个算术运算的结果[0x1337+0777]或标签[label]指定。

示例:全局变量

在C/C++中,全局变量在程序的任何地方都可以使用。为了实现这一点,全局变量在内存中的地址是固定的,当程序运行在各种栈帧中时,它们并不会移动。

这就意味着,在汇编过程中变量的确切地址总是已知的。因此,全局变量将使用绝对寻址来访问,例如使用mov eax,[0x1000]。

2.6.2 间接寻址

间接寻址使用寄存器来指定地址。所用寄存器包括16位通用寄存器(如[ax])和32位通用寄存器(如[eax])。但是,8位通用寄存器(如al、bh等)和特殊寄存器不能用于寻址。

示例:指针

许多编程语言都使用指针(pointer)的概念,有些是直接使用,有些则在幕后运行。直接使用和操作指针是C/C++数据类型进行间接寻址的一个例子。C程序中可能会有这样的代码:int x=1; int*p=&x;。在这里,指针p被设定为指向x。即使你对C语言不熟悉,也不用担心,你只需知道p保存的是x在内存中的地址。

然而,p的值可能会改变以指向其他事物,因此它的目标地址并不固定。要在汇编中访问p指示的值,首先要将p加载到一个寄存器中,然后用这个寄存器来查找我们想要的值。这一过程在以下的x86指令中有所展示:

2.6.3 基址加偏移量寻址

一些变量(比如数组)是通过基址和偏移量在内存中储存的。我们可以使用基址和偏移量来访问数组内的单个值。

基址加偏移量寻址或基址寻址使用寄存器的值和偏移量来指示地址。这种寻址模式通常用于访问数组。例如,在某种语言中,你可能写了myList[8],这表示从myList的基址开始,你正在访问第八个元素。在汇编语言中,[eax+8]表示从数组基址(存储在eax中)开始的第八个字节:

2.6.4 索引寻址

如果数组中的元素总是一个字节长,则基址加偏移量的寻址模式运作良好。对于元素更大的数组,偏移量必须手动计算,这很烦琐,也容易出错。

在这些情况下,索引寻址可能是更好的选择。索引寻址使用一个索引寄存器、一个比例因子和一个偏移量来指定地址。这个比例因子必须是1、2、4或8。

示例:数组

让我们定义一个整型数组,即int x[100];。这表示声明了一个包含100个整数的数组。在内存中,数组中的每个值都存储在基址的特定偏移位置。这个偏移量是由数组中的值(比如一个32位或4字节的整数)的大小决定的。

假设整型数组是在偏移量0x1000的位置创建的。如果 n 存储在ebx中,则下面的指令会将数组的第 n 个元素移动到eax中:

2.6.5 基址-索引寻址

基址-索引寻址组合了索引寻址和基址加偏移量寻址的元素。它使用一个基址寄存器、一个索引寄存器、一个比例因子(1、2、4或8)和一个偏移量进行寻址。

例如,地址[ebx+edi*4+0x1000]的基址存储在ebx中,索引存储在edi中,并且偏移量为0x1000。

示例:结构体

基址-索引寻址对于访问嵌套数据类型的元素来说是非常理想的。例如,C语言命令struct {int i; short a[4];} s;创建了一个包含多个字段的结构体(struct),其中包括一个数组。

这个结构体中的每个元素都位于特定的偏移位置,这意味着数组a有一定的偏移量或基址。然而,a中包含的元素也各自有相对这个基址的不同偏移量。

假定结构体s的基址存储在ebx中,并且数组a存储在离此基址4字节的地方。如果 n 存储在ecx中,那么下面的指令将访问a中的第 n 个元素:

如果你觉得更高级的寻址方式难以理解,不用太挫败。操作系统一直在隐藏内存,所以思考数组在内存中的存储方式是一个全新的领域。这些寻址模式首先需要作为理论引入,但在你开始在实际的汇编代码中看到它们被使用以前,它们理解起来可能会比较困难。但别担心,你终将会理解的。 TPdh8Qnsd6Mg1BYuDsdABoEneO2RZc/hat2owUaX4bybxkzpvN4o0xqJa9R1VZL8

点击中间区域
呼出菜单
上一章
目录
下一章
×