MOV 和 LEA 的区别是什么?

我想知道这些说明书之间的区别是什么:

MOV AX, [TABLE-ADDR]

还有

LEA AX, [TABLE-ADDR]
225474 次浏览

如果只指定文字,则没有区别。LEA 有更多的能力,你可以在这里读到:

Http://www.oopweb.com/assembly/documents/artofassembly/volume/chapter_6/ch06-1.html#heading1-136

  • LEA表示负载有效地址
  • MOV表示负载值

简而言之,LEA加载一个指向您正在寻址的项目的指针,而 MOV 加载该地址的实际值。

LEA的用途是允许执行一个非平凡的地址计算并存储结果[以供以后使用]

LEA ax, [BP+SI+5] ; Compute address of value


MOV ax, [BP+SI+5] ; Load value at that address

在只涉及常量的情况下,MOV(通过汇编程序的常量计算)有时似乎与使用 LEA的最简单情况重叠。如果您有一个包含多个基地址等的多部分计算,那么它是非常有用的。

它取决于使用过的汇编程序,因为

mov ax,table_addr

在 MASM 工程作为

mov ax,word ptr[table_addr]

因此它加载 table_addr的第一个字节,而不是偏移量到 table_addr

mov ax,offset table_addr

或者

lea ax,table_addr

效果是一样的。

如果 table_addr是一个局部变量,则 lea版本也可以正常工作。

some_procedure proc


local table_addr[64]:word


lea ax,table_addr

区别很微妙,但很重要。MOV 指令实际上是 TABLE-ADDR 标签所代表的地址的一个“移动”副本。LEA 指令是一个“加载有效地址”,它是一个间接指令,这意味着 TABLE-ADDR 指向要加载的地址所在的内存位置。

有效地使用 LEA 相当于在 C 等语言中使用指针,因此它是一个强大的指令。

指令 MOV reg,addr 表示将存储在地址 addr 的变量读入寄存器 reg。指令 LEA reg,addr 意味着将地址(而不是存储在地址中的变量)读入寄存器 reg。

MOV 指令的另一种形式是 MOV reg,immdata 是指将即时数据(即常数)读入寄存器 reg。注意,如果 LEA reg 中的 addr 只是一个常量(即一个固定偏移量) ,那么 LEA 指令本质上与一个等效的 MOV reg、 immdata 指令完全相同,它加载与即时数据相同的常量。

在 NASM 语法中:

mov eax, var       == lea eax, [var]   ; i.e. mov r32, imm32
lea eax, [var+16]  == mov eax, var+16
lea eax, [eax*4]   == shl eax, 2        ; but without setting flags

在 MASM 语法中,使用 OFFSET var来获得 mov 即时而不是加载。

基本上... “移动到 REG... 在计算之后...” 它似乎还有其它用途:)

如果你忘了这个值是一个指针 你可以用它来进行代码优化/最小化... ... 不管怎样... ..。

MOV EBX , 1
MOV ECX , 2


;//with 1 instruction you got result of 2 registers in 3rd one ...
LEA EAX , [EBX+ECX+5]

EAX = 8

一开始是这样的:

MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5

LEA (负载有效地址)是一种移位加法指令。它被添加到8086,因为硬件有解码和计算寻址模式。

如其他答案所述:

  • MOV将获取括号内的地址 资料,并将该 资料放入目标操作数。
  • LEA将执行括号内地址的 计算,并将该 计算地址放入目标操作数。这种情况并没有真正进入内存并获取数据。LEA所做的工作是计算“有效地址”。

因为内存可以通过几种不同的方式来寻址(参见下面的例子) ,所以有时使用 LEA来添加或相乘寄存器,而不使用显式的 ADDMUL指令(或等效指令)。

因为每个人都在展示 Intel 语法的例子,这里有一些 AT & T 语法的例子:

MOVL 16(%ebp), %eax       /* put long  at  ebp+16  into eax */
LEAL 16(%ebp), %eax       /* add 16 to ebp and store in eax */


MOVQ (%rdx,%rcx,8), %rax  /* put qword at  rcx*8 + rdx  into rax */
LEAQ (%rdx,%rcx,8), %rax  /* put value of "rcx*8 + rdx" into rax */


MOVW 5(%bp,%si), %ax      /* put word  at  si + bp + 5  into ax */
LEAW 5(%bp,%si), %ax      /* put value of "si + bp + 5" into ax */


MOVQ 16(%rip), %rax       /* put qword at rip + 16 into rax                 */
LEAQ 16(%rip), %rax       /* add 16 to instruction pointer and store in rax */


MOVL label(,1), %eax      /* put long at label into eax            */
LEAL label(,1), %eax      /* put the address of the label into eax */

让我们通过一个例子来理解这一点。

[俄语] 还有

[拉丁语] 假设 ebx 中的值为0x400000。然后 mov 将转到地址0x400000并复制4字节的数据显示它们的 eax 寄存器。而 lea 将把地址0x400000复制到 eax 中。因此,在执行每种情况下 eax 的每个指令值之后都将是(假设在内存中0x400000容量为30)。

Eax = 30(如属移动) Eax = 0x400000(如属 lea) 对于定义,mov 将数据从 rm32复制到目的地(mov destrm32) ,lea (加载有效地址)将把地址复制到目的地(mov destrm32)。

以前的答案没有一个能解开我的疑惑,所以我想加上我自己的。

我所忽略的是,lea操作对待括号的使用方式与 mov不同。

考虑一下 C,假设我有一个 long数组,我称之为 array。现在,表达式 array[i]执行解引用,从内存中加载地址 array + i * sizeof(long)[1]的值。

另一方面,考虑表达式 &array[i]。这仍然包含子表达式 array[i],但是不执行解引用!array[i]的含义已经改变。它不再意味着执行顺从,而是作为一种 规格,告诉 &我们正在寻找的内存地址。如果您愿意,您可以将 &视为“抵消”解引用。

因为两个用例在许多方面相似,所以它们共享语法 array[i],但是 &的存在与否改变了解释该语法的方式。如果没有 &,它就是一个解引用,实际上是从数组中读取的。对于 &来说,就不是了。值 array + i * sizeof(long)仍在计算中,但不会取消引用。

这种情况与 movlea非常相似。对于 mov,会发生 lea不会发生的解引用。尽管两者都使用了括号。例如,movq (%r8), %r9leaq (%r8), %r9。对于 mov,这些括号表示“解引用”; 对于 lea,它们不表示。这类似于 array[i]在没有 &时只表示“解引用”。

举个例子。

考虑一下代码

movq (%rdi, %rsi, 8), %rbp

这会将位于内存位置 %rdi + %rsi * 8的值加载到寄存器 %rbp中。即: 获取寄存器 %rdi中的值和寄存器 %rsi中的值。将后者乘以8,然后将其与前者相加。并将其放入寄存器 %rbp中。

这段代码对应于 C 行 x = array[i];,其中 array变成 %rdii变成 %rsix变成 %rbp8是数组中包含的数据类型的长度。

现在考虑使用 lea的类似代码:

leaq (%rdi, %rsi, 8), %rbp

正如使用 movq对应于解引用一样,这里使用 leaq对应于 没有解引用。这条装配线对应于 C 线 x = &array[i];。回想一下,&array[i]的含义从取消引用改为简单地指定一个位置。同样,使用 leaq(%rdi, %rsi, 8)的含义从取消引用改为指定位置。

这行代码的语义如下: 获取寄存器 %rdi中的值和寄存器 %rsi中的值。将后者乘以8,然后将其与前者相加。将此值放入寄存器 %rbp中。不涉及内存负载,只涉及算术运算[2]。

请注意,我对 leaqmovq的描述之间的唯一区别是,movq做了解引用,而 leaq没有。事实上,要编写 leaq描述,我基本上是复制 + 粘贴 movq的描述,然后删除“在这个位置查找值”。

总结一下: movqleaq比较棘手,因为它们对待括号的使用是不同的,比如在 (%rsi)(%rdi, %rsi, 8)中。在 movq(和除 lea之外的所有其他指令)中,这些括号表示真正的解引用,而在 leaq中,它们不表示,而且是纯粹方便的语法。


[1]我已经说过,当 arraylong的数组时,表达式 array[i]将从地址 array + i * sizeof(long)加载该值。这是真的,但有一个微妙的地方需要解决。如果我写 C 代码

long x = array[5];

这是 没有,和打字一样

long x = *(array + 5 * sizeof(long));

它似乎是 应该的基础上,我以前的陈述,但它不是。

C 指针加法有一个窍门。假设我有一个指针 p指向类型为 T的值。表达式 p + i does 没有表示“ p处的位置加上 i字节”。相反,表达式 p + i事实上表示“ p处的位置加上 i * sizeof(T)字节”。

这种方法的便利之处在于,要获得“下一个值”,我们只需要编写 p + 1而不是 p + 1 * sizeof(T)

这意味着 C 代码 long x = array[5];实际上等效于

long x = *(array + 5)

因为 C 会自动将 5乘以 sizeof(long)

那么,在这个 StackOverflow 问题的上下文中,这一切是如何相关的呢?这意味着当我说“地址 array + i * sizeof(long)”时,我做 没有的意思是“ array + i * sizeof(long)”被解释为一个 C 表达式。为了让我的答案更加明确,我自己正在做 sizeof(long)的乘法运算,但是由于这个原因,这个表达式不应该被读作 C,就像普通的使用 C 语法的数学一样。

[2]附注: 因为 lea所做的全部都是算术运算,它的参数实际上并不需要引用有效的地址。由于这个原因,它通常用于对可能不会被解引用的值执行纯算术。例如,具有 -O2优化的 cc转换为

long f(long x) {
return x * 5;
}

转换为以下内容(去掉不相关的行) :

f:
leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
ret

MOV 可以做 LEA [ label ]相同的事情,但是 MOV 指令包含指令本身内部的有效地址作为一个直接常量(由汇编程序预先计算)。LEA 使用 PC- 相对来计算指令执行期间的有效地址。