MOV 和 LEA 有什么区别?
我想知道这些说明之间的区别是什么:
MOV AX, [TABLE-ADDR]
和
LEA AX, [TABLE-ADDR]
I would like to know what the difference between these instructions is:
MOV AX, [TABLE-ADDR]
and
LEA AX, [TABLE-ADDR]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
LEA
表示加载有效地址MOV
表示加载值简而言之,
LEA
加载指向您正在寻址的项目的指针,而 MOV 加载实际值在那个地址。LEA
的目的是允许执行一项重要的地址计算并存储结果[供以后使用],其中仅涉及常量,
MOV
(通过汇编程序的常量计算)有时可能会与LEA
最简单的使用情况重叠。如果您有多个基地址等的多部分计算,它很有用。LEA
means Load Effective AddressMOV
means Load ValueIn short,
LEA
loads a pointer to the item you're addressing whereas MOV loads the actual value at that address.The purpose of
LEA
is to allow one to perform a non-trivial address calculation and store the result [for later usage]Where there are just constants involved,
MOV
(through the assembler's constant calculations) can sometimes appear to overlap with the simplest cases of usage ofLEA
. Its useful if you have a multi-part calculation with multiple base addresses etc.在 NASM 语法中:
在 MASM 语法中,使用
OFFSET var
获取 mov-immediate 而不是加载。In NASM syntax:
In MASM syntax, use
OFFSET var
to get a mov-immediate instead of a load.之前的答案都没有完全解决我自己的困惑,所以我想添加我自己的答案。
我缺少的是 lea 操作对括号的使用与 mov 的处理方式不同。
想想 C。假设我有一个
long
数组,我称之为array
。现在,表达式 array[i] 执行取消引用,从地址 array + i * sizeof(long) [1] 处的内存加载值。另一方面,考虑表达式
&array[i]
。这仍然包含子表达式 array[i],但不执行取消引用!array[i]
的含义已更改。它不再意味着执行遵从,而是充当一种规范,告诉&
我们正在寻找什么内存地址。如果您愿意,您也可以将&
视为“取消”取消引用。由于这两个用例在很多方面都很相似,因此它们共享语法
array[i]
,但&
的存在或不存在会改变该语法的解释方式。如果没有&
,它是取消引用并实际上从数组中读取。对于&
来说,情况并非如此。值array + i * sizeof(long)
仍会计算,但不会取消引用。mov
和lea
的情况非常相似。使用mov
会发生取消引用,而使用lea
则不会发生这种情况。尽管两者都使用了括号,但情况仍然如此。例如,movq (%r8)、%r9
和leaq (%r8)、%r9
。对于mov
,这些括号意味着“取消引用”;对于lea
,他们不会。这类似于当没有&
时array[i]
仅表示“取消引用”。下面是一个例子。
考虑以下代码
:它将内存位置
%rdi + %rsi * 8
处的值加载到寄存器%rbp
中。即:获取寄存器%rdi
中的值和寄存器%rsi
中的值。将后者乘以 8,然后与前者相加。 找到此位置的值并将其放入寄存器%rbp
中。此代码对应于 C 行
x = array[i];
,其中array
变为%rdi
和i
变为%rsi
,x
变为%rbp
。8
是数组中包含的数据类型的长度。现在考虑使用
lea
的类似代码:正如使用
movq
对应于取消引用一样,这里使用leaq
对应于 not 取消引用。这行汇编代码对应于 C 行x = &array[i];
。回想一下,&
将array[i]
的含义从解除引用更改为简单地指定位置。同样,使用leaq
会将(%rdi, %rsi, 8)
的含义从取消引用更改为指定位置。这行代码的语义如下:获取寄存器
%rdi
中的值和寄存器%rsi
中的值。将后者乘以 8,然后与前者相加。将此值放入寄存器%rbp
中。不涉及内存加载,仅涉及算术运算 [2]。请注意,我对 leaq 和 movq 的描述之间的唯一区别是 movq 进行了取消引用,而 leaq 则没有't。事实上,为了编写
leaq
描述,我基本上复制+粘贴了movq
的描述,然后删除了“在这个位置查找值”。总结一下:
movq
与leaq
比较棘手,因为它们对待括号的使用,如(%rsi)
和(% rdi,%rsi,8)
,不同。在 movq(以及除 lea 之外的所有其他指令)中,这些括号表示真正的取消引用,而在 leaq 中它们不是,并且纯粹是方便的语法。[1] 我已经说过,当
array
是一个long
数组时,表达式array[i]
从地址array[i]
加载值代码>数组+ i * sizeof(long)。这是事实,但有一个微妙之处需要解决。如果我编写 C 代码,这与键入它不相同,
它似乎应该基于我之前的陈述,但事实并非如此。
发生的事情是 C 指针加法有一个技巧。假设我有一个指针
p
指向T
类型的值。表达式p + i
确实不表示“p
处的位置加上i
字节”。相反,表达式p + i
实际上 表示“p
处的位置加上i * sizeof(T)
字节”。这样做的便利之处在于,要获取“下一个值”,我们只需编写
p + 1
而不是p + 1 * sizeof(T)
。这意味着 C 代码
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
会转换为以下内容(删除了不相关的行):
None of the previous answers quite got to the bottom of my own confusion, so I'd like to add my own.
What I was missing is that
lea
operations treat the use of parentheses different than howmov
does.Think of C. Let's say I have an array of
long
that I callarray
. Now the expressionarray[i]
performs a dereference, loading the value from memory at the addressarray + i * sizeof(long)
[1].On the other hand, consider the expression
&array[i]
. This still contains the sub-expressionarray[i]
, but no dereferencing is performed! The meaning ofarray[i]
has changed. It no longer means to perform a deference but instead acts as a kind of a specification, telling&
what memory address we're looking for. If you like, you could alternatively think of the&
as "cancelling out" the dereference.Because the two use-cases are similar in many ways, they share the syntax
array[i]
, but the existence or absence of a&
changes how that syntax is interpreted. Without&
, it's a dereference and actually reads from the array. With&
, it's not. The valuearray + i * sizeof(long)
is still calculated, but it is not dereferenced.The situation is very similar with
mov
andlea
. Withmov
, a dereference occurs that does not happen withlea
. This is despite the use of parentheses that occurs in both. For instance,movq (%r8), %r9
andleaq (%r8), %r9
. Withmov
, these parentheses mean "dereference"; withlea
, they don't. This is similar to howarray[i]
only means "dereference" when there is no&
.An example is in order.
Consider the code
This loads the value at the memory location
%rdi + %rsi * 8
into the register%rbp
. That is: get the value in the register%rdi
and the value in the register%rsi
. Multiply the latter by 8, and then add it to the former. Find the value at this location and place it into the register%rbp
.This code corresponds to the C line
x = array[i];
, wherearray
becomes%rdi
andi
becomes%rsi
andx
becomes%rbp
. The8
is the length of the data type contained in the array.Now consider similar code that uses
lea
:Just as the use of
movq
corresponded to dereferencing, the use ofleaq
here corresponds to not dereferencing. This line of assembly corresponds to the C linex = &array[i];
. Recall that&
changes the meaning ofarray[i]
from dereferencing to simply specifying a location. Likewise, the use ofleaq
changes the meaning of(%rdi, %rsi, 8)
from dereferencing to specifying a location.The semantics of this line of code are as follows: get the value in the register
%rdi
and the value in the register%rsi
. Multiply the latter by 8, and then add it to the former. Place this value into the register%rbp
. No load from memory is involved, just arithmetic operations [2].Note that the only difference between my descriptions of
leaq
andmovq
is thatmovq
does a dereference, andleaq
doesn't. In fact, to write theleaq
description, I basically copy+pasted the description ofmovq
, and then removed "Find the value at this location".To summarize:
movq
vs.leaq
is tricky because they treat the use of parentheses, as in(%rsi)
and(%rdi, %rsi, 8)
, differently. Inmovq
(and all other instruction exceptlea
), these parentheses denote a genuine dereference, whereas inleaq
they do not and are purely convenient syntax.[1] I've said that when
array
is an array oflong
, the expressionarray[i]
loads the value from the addressarray + i * sizeof(long)
. This is true, but there's a subtlety that should be addressed. If I write the C codethis is not the same as typing
It seems that it should be based on my previous statements, but it's not.
What's going on is that C pointer addition has a trick to it. Say I have a pointer
p
pointing to values of typeT
. The expressionp + i
does not mean "the position atp
plusi
bytes". Instead, the expressionp + i
actually means "the position atp
plusi * sizeof(T)
bytes".The convenience of this is that to get "the next value" we just have to write
p + 1
instead ofp + 1 * sizeof(T)
.This means that the C code
long x = array[5];
is actually equivalent tobecause C will automatically multiply the
5
bysizeof(long)
.So in the context of this StackOverflow question, how is this all relevant? It means that when I say "the address
array + i * sizeof(long)
", I do not mean for "array + i * sizeof(long)
" to be interpreted as a C expression. I am doing the multiplication bysizeof(long)
myself in order to make my answer more explicit, but understand that due to that, this expression should not be read as C. Just as normal math that uses C syntax.[2] Side note: because all
lea
does is arithmetic operations, its arguments don't actually have to refer to valid addresses. For this reason, it's often used to perform pure arithmetic on values that may not be intended to be dereferenced. For instance,cc
with-O2
optimization translatesinto the following (irrelevant lines removed):
MOV reg,addr 指令的意思是将地址addr 处存储的变量读取到寄存器reg 中。 LEA reg,addr 指令表示将地址(不是该地址存储的变量)读入寄存器 reg 中。
MOV指令的另一种形式是MOV reg,immdata,意思是将立即数(即常量)immdata读入寄存器reg。请注意,如果 LEA reg,addr 中的 addr 只是一个常量(即固定偏移量),则该 LEA 指令本质上与加载相同常量作为立即数据的等效 MOV reg,immdata 指令完全相同。
The instruction MOV reg,addr means read a variable stored at address addr into register reg. The instruction LEA reg,addr means read the address (not the variable stored at the address) into register reg.
Another form of the MOV instruction is MOV reg,immdata which means read the immediate data (i.e. constant) immdata into register reg. Note that if the addr in LEA reg,addr is just a constant (i.e. a fixed offset) then that LEA instruction is essentially exactly the same as an equivalent MOV reg,immdata instruction that loads the same constant as immediate data.
如果仅指定文字,则没有区别。不过,LEA 有更多功能,您可以在这里阅读它们:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
If you only specify a literal, there is no difference. LEA has more abilities, though, and you can read about them here:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
正如其他答案中所述:
MOV
将抓取括号内地址处的数据,并将该数据放入目标操作数中。LEA
将对括号内的地址执行计算,并将计算出的地址放入目标操作数中。这种情况的发生并没有实际进入内存并获取数据。LEA
所做的工作是计算“有效地址”。由于可以通过多种不同的方式对内存进行寻址(请参阅下面的示例),
LEA
有时用于将寄存器相加或相乘,而不使用显式的ADD
或MUL< /code> 指令(或等效指令)。
由于每个人都在展示 Intel 语法的示例,因此这里有一些 AT&T 语法:
As stated in the other answers:
MOV
will grab the data at the address inside the brackets and place that data into the destination operand.LEA
will perform the calculation of the address inside the brackets and place that calculated address into the destination operand. This happens without actually going out to the memory and getting the data. The work done byLEA
is in the calculating of the "effective address".Because memory can be addressed in several different ways (see examples below),
LEA
is sometimes used to add or multiply registers together without using an explicitADD
orMUL
instruction (or equivalent).Since everyone is showing examples in Intel syntax, here are some in AT&T syntax:
这取决于所使用的汇编器,因为
在 MASM 中,它的工作原理是
,它从
table_addr
加载第一个字节,而不是table_addr
的偏移量。您应该改用或 ,
其效果相同。
如果
table_addr
是局部变量,lea
版本也可以正常工作,例如It depends on the used assembler, because
in MASM works as
So it loads the first bytes from
table_addr
and NOT the offset totable_addr
. You should use insteador
which works the same.
lea
version also works fine iftable_addr
is a local variable e.g.基本上......“计算后进入REG......”
它似乎也适用于其他目的:)
如果您忘记该值是一个指针,
你可以用它来进行代码优化/最小化......无论如何......
EAX = 8
原来它是:
Basically ... "Move into REG ... after computing it..."
it seems to be nice for other purposes as well :)
if you just forget that the value is a pointer
you can use it for code optimizations/minimization ...what ever..
EAX = 8
originaly it would be:
让我们通过一个例子来理解这一点。
移动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)。
Lets understand this with a example.
mov eax, [ebx]
and
lea eax, [ebx]
Suppose value in ebx is 0x400000. Then mov will go to address 0x400000 and copy 4 byte of data present their to eax register.Whereas lea will copy the address 0x400000 into eax. So, after the execution of each instruction value of eax in each case will be (assuming at memory 0x400000 contain is 30).
eax = 30 (in case of mov)
eax = 0x400000 (in case of lea)
For definition mov copy the data from rm32 to destination (mov dest rm32) and lea(load effective address) will copy the address to destination (mov dest rm32).
MOV 可以做与 LEA [标签] 相同的事情,但 MOV 指令包含指令本身的有效地址作为立即数(由汇编器预先计算)。 LEA 使用 PC-relative 来计算指令执行期间的有效地址。
MOV can do same thing as LEA [label], but MOV instruction contain the effective address inside the instruction itself as an immediate constant (calculated in advance by the assembler). LEA uses PC-relative to calculate the effective address during the execution of the instruction.
差异很微妙但很重要。 MOV 指令是“MOVe”,实际上是 TABLE-ADDR 标签所代表的地址的副本。 LEA 指令是“加载有效地址”,它是一种间接指令,这意味着 TABLE-ADDR 指向找到要加载的地址的内存位置。
有效地使用 LEA 相当于在 C 等语言中使用指针,因此它是一个强大的指令。
The difference is subtle but important. The MOV instruction is a 'MOVe' effectively a copy of the address that the TABLE-ADDR label stands for. The LEA instruction is a 'Load Effective Address' which is an indirected instruction, which means that TABLE-ADDR points to a memory location at which the address to load is found.
Effectively using LEA is equivalent to using pointers in languages such as C, as such it is a powerful instruction.
LEA(加载有效地址)是移位加法指令。它被添加到8086中是因为硬件可以解码和计算寻址模式。
LEA (Load Effective Address) is a shift-and-add instruction. It was added to 8086 because hardware is there to decode and calculate adressing modes.