167

我想知道这些指令之间的区别是什么:

MOV AX, [TABLE-ADDR]

LEA AX, [TABLE-ADDR]
4

12 回答 12

211
  • 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. 如果您有多个基地址等的多部分计算,它很有用。

于 2009-11-09T08:40:21.303 回答
55

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

于 2013-06-27T14:37:58.167 回答
33

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

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

于 2009-11-09T09:03:50.953 回答
17

以前的答案都没有完全解决我自己的困惑,所以我想添加我自己的。

我缺少的是lea操作处理括号的使用与如何处理不同mov

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

另一方面,考虑表达式&array[i]。这仍然包含子表达式array[i],但不执行解引用!的意思array[i]变了。它不再意味着执行尊重,而是充当一种规范,告诉&我们正在寻找的内存地址。如果您愿意,您也可以将其&视为“取消”取消引用。

因为这两个用例在很多方面都很相似,所以它们共享语法array[i],但是存在或不存在 a&会改变该语法的解释方式。没有&,它是一个取消引用,实际上是从数组中读取的。,&它不是。该值array + i * sizeof(long)仍在计算中,但未取消引用。

mov情况与和非常相似lea。使用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];, where arraybecome%rdiibecome%rsixbecome %rbp8是包含在数组中的数据类型的长度。

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

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

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

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

leaq请注意,我对and的描述之间的唯一区别movqmovq取消引用,而leaq没有。其实写leaq描述,我基本上是复制+粘贴的描述movq,然后去掉“在这个位置找值”。

总结一下:movqvs.leaq很棘手,因为它们对括号的使用,如(%rsi)and (%rdi, %rsi, 8),不同。在movq(以及除 之外的所有其他指令lea)中,这些括号表示真正的取消引用,而在leaq它们没有并且纯粹是方便的语法。


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

long x = array[5];

这和打字一样

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

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

发生的事情是 C 指针添加有一个技巧。假设我有一个指向ptype 值的指针T。该表达式p + i并不意味着pi字节的位置”。相反,该表达式p + i 实际上表示“pi * 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的都是算术运算,所以它的参数实际上不必引用有效地址。出于这个原因,它通常用于对可能不打算取消引用的值执行纯算术运算。例如,cc通过-O2优化翻译

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

进入以下(删除不相关的行):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret
于 2020-02-18T00:37:25.617 回答
10

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

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

于 2009-11-09T08:38:24.220 回答
9

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

mov ax,table_addr

在 MASM 中作为

mov ax,word ptr[table_addr]

因此,它将第一个字节table_addr从而不是偏移量加载到table_addr. 你应该改用

mov ax,offset table_addr

或者

lea ax,table_addr

它的工作原理相同。

leatable_addr如果是局部变量,版本也可以正常工作,例如

some_procedure proc

local table_addr[64]:word

lea ax,table_addr
于 2009-11-09T08:44:24.650 回答
7

如其他答案所述:

  • 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 */
于 2019-11-21T18:21:36.137 回答
3

基本上......“移动到 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
于 2017-10-19T22:07:58.070 回答
2

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

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 )。

于 2019-11-24T22:48:23.803 回答
1

MOV 可以做与 LEA [label] 相同的事情,但 MOV 指令包含指令本身内部的有效地址作为立即常数(由汇编程序预先计算)。LEA 在指令执行期间使用 PC-relative 计算有效地址。

于 2020-06-22T05:59:43.110 回答
0

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

于 2019-11-21T15:39:59.817 回答
-1

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

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

于 2009-11-09T08:46:29.757 回答