MOV和LEA有什么区别?

时间:2009-11-09 08:32:08

标签: assembly x86 instruction-set

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

MOV AX, [TABLE-ADDR]

LEA AX, [TABLE-ADDR]

12 个答案:

答案 0 :(得分:130)

  • 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使用情况重叠。如果您使用多个基址等进行多部分计算,则它非常有用。

答案 1 :(得分:40)

在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-immediate而不是加载。

答案 2 :(得分:23)

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

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

答案 3 :(得分:10)

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

http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136

答案 4 :(得分:10)

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

mov ax,table_addr
MASM中的

作为

mov ax,word ptr[table_addr]

因此它从table_addr加载第一个字节而不是table_addr的偏移量。你应该使用

mov ax,offset table_addr

lea ax,table_addr

也是如此。

如果lea是本地变量,

table_addr版本也可以正常工作,例如

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

答案 5 :(得分:9)

以前的答案都没有引起我的困惑,所以我想添加自己的答案。

我所缺少的是,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变成%rdi,而i变成%rsi,而x变成{{1 }}。 %rbp是数组中包含的数据类型的长度。

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

lea

就像使用leaq (%rdi, %rsi, 8), %rbp 对应于取消引用一样,此处使用movq对应于取消引用。此汇编行对应于C行leaq。回想一下,x = &array[i];&的含义从取消引用更改为仅指定位置。同样,array[i]的使用将leaq的含义从取消引用改为指定位置。

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

请注意,我对%rbpleaq的描述之间唯一的区别是movq进行了取消引用,而movq则没有。实际上,要编写leaq描述,我基本上是复制并粘贴了leaq的描述,然后删除了“在此位置查找值”。

总结:movqmovq的比较棘手,因为它们像leaq(%rsi)一样对待括号的使用也有所不同。在(%rdi, %rsi, 8)(以及movq以外的所有其他指令)中,这些括号表示真正的取消引用,而在lea中则不是,并且纯粹是方便的语法。


[1]我已经说过,当leaqarray的数组时,表达式long从地址array[i]加载值。的确如此,但是有一个微妙之处需要解决。如果我写C代码

array + i * sizeof(long)

这与键入不相同

long x = array[5];

似乎应该基于我以前的陈述,但事实并非如此。

这是C指针加法的一个窍门。假设我有一个指针long x = *(array + 5 * sizeof(long)); 指向类型p的值。表达式T并不是 的意思是“ p + i加上p个字节的位置”。相反,表达式i实际上 的意思是“ p + i加上p字节的位置”。

这样做的方便之处在于,要获取“下一个值”,我们只需要写i * sizeof(T)而不是p + 1

这意味着C代码p + 1 * sizeof(T)实际上等效于

long x = array[5];

因为C会将long x = *(array + 5) 乘以5

那么在这个StackOverflow问题的背景下,这一切有何关系?这意味着当我说“地址sizeof(long)”时,我并不是说将“ array + i * sizeof(long)”解释为C表达式。我正在自己array + i * sizeof(long)进行乘法运算,以便使答案更明确,但应理解,由于这个原因,该表达式不应被理解为C。就像使用C语法的普通数学一样。

[2]旁注:由于所有sizeof(long)都是算术运算,因此其参数实际上不必引用有效地址。因此,它通常用于对可能不打算取消引用的值执行纯算术。例如,leacc优化可以翻译

-O2

分为以下内容(删除了不相关的行):

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

答案 6 :(得分:2)

基本上......“在计算之后进入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

答案 7 :(得分:1)

如其他答案所述:

  • 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 */

答案 8 :(得分:1)

MOV可以执行与LEA [label]相同的操作,但是MOV指令包含指令内部的有效地址作为立即数常量(由汇编程序预先计算)。 LEA使用PC相对值来计算指令执行期间的有效地址。

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

mov eax,[ebx] 和

lea eax,[ebx] 假设ebx中的值为0x400000。然后mov将到达地址0x400000并将4个字节的数据复制到eax寄存器中,而lea会将地址0x400000复制到eax中。因此,在每种情况下执行eax的每条指令后,值都会是(假设在内存0x400000中包含的是30)。

eax = 30(如果是mov) eax = 0x400000(如果是lea) 对于mov定义,将数据从rm32复制到目标(mov dest rm32),而lea(加载有效地址)会将地址复制到目标(mov dest rm32)。

答案 11 :(得分:-1)

差异很微妙但很重要。 MOV指令是一个'MOVe',实际上是TABLE-ADDR标签所代表的地址的副本。 LEA指令是一个'加载有效地址',它是一个间接指令,这意味着TABLE-ADDR指向一个存储位置,在该存储位置找到要加载的地址。

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