正如其他人指出的那样,LEA(有效负载地址)通常被用作进行某些计算的 “技巧”,但这并不是其主要目的。 x86 指令集旨在支持 Pascal 和 C 等高级语言,在这些语言中,数组(尤其是 int 或小结构的数组)很常见。例如,考虑一个表示(x,y)坐标的结构:
struct Point
{
int xcoord;
int ycoord;
};
现在想象一下一条语句:
int y = points[i].ycoord;
其中points[]
Point
的数组。假设数组的基础已经在EBX
,并且变量i
在EAX
,并且xcoord
和ycoord
均为 32 位(因此ycoord
在 struct 中的偏移量为 4 个字节),则可以将该语句编译为:
MOV EDX, [EBX + 8*EAX + 4] ; right side is "effective address"
这将土地y
在EDX
。比例因子 8 是因为每个Point
大小为 8 个字节。现在考虑与运算符&的 “地址” 一起使用的表达式:
int *p = &points[i].ycoord;
在这种情况下,您不需要ycoord
的值,但需要它的地址。这就是LEA
(有效负载地址)的来源。编译器可以生成MOV
LEA ESI, [EBX + 8*EAX + 4]
这将在ESI
加载地址。
摘自 Abrash的 “汇编禅”:
LEA
,这是唯一执行内存寻址计算但实际上并未寻址内存的指令。LEA
接受标准的内存寻址操作数,但只不过将计算出的内存偏移量存储在指定的寄存器中即可,该寄存器可以是任何通用寄存器。这给了我们什么?
ADD
不提供的两件事:
- 使用两个或三个操作数执行加法的能力,以及
- 将结果存储在任何寄存器中的能力;不只是源操作数之一。
LEA
不会更改标志。
例子
LEA EAX, [ EAX + EBX + 1234567 ]
计算EAX + EBX + 1234567
(这是三个操作数)LEA EAX, [ EBX + ECX ]
计算EBX + ECX
而不会覆盖任何结果。LEA EAX, [ EBX + N * EBX ]
(N 可以是 1,2,4,8)。其他用例在循环中很方便: LEA EAX, [ EAX + 1 ]
和INC EAX
之间的区别在于,后者改变了EFLAGS
而前者却没有改变;这保留了CMP
状态。
LEA
指令的另一个重要特征是,它不会更改条件代码(例如CF
和ZF
,而通过算术指令(例如ADD
或MUL
计算地址。此功能降低了指令之间的依赖性,从而为编译器或硬件调度程序的进一步优化留出了空间。