正如其他人指出的那样,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计算地址。此功能降低了指令之间的依赖性,从而为编译器或硬件调度程序的进一步优化留出了空间。