我想知道这些说明之间的区别是什么:
MOV AX, [TABLE-ADDR]
和
LEA AX, [TABLE-ADDR]
答案 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)
仍在计算中,但未取消引用。
mov
和lea
的情况非常相似。对于mov
,将发生lea
不会发生的取消引用。尽管在这两者中都使用了括号,但是这是不可行的。例如,movq (%r8), %r9
和leaq (%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]。
请注意,我对%rbp
和leaq
的描述之间唯一的区别是movq
进行了取消引用,而movq
则没有。实际上,要编写leaq
描述,我基本上是复制并粘贴了leaq
的描述,然后删除了“在此位置查找值”。
总结:movq
与movq
的比较棘手,因为它们像leaq
和(%rsi)
一样对待括号的使用也有所不同。在(%rdi, %rsi, 8)
(以及movq
以外的所有其他指令)中,这些括号表示真正的取消引用,而在lea
中则不是,并且纯粹是方便的语法。
[1]我已经说过,当leaq
是array
的数组时,表达式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)
都是算术运算,因此其参数实际上不必引用有效地址。因此,它通常用于对可能不打算取消引用的值执行纯算术。例如,lea
和cc
优化可以翻译
-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
有时用于在不使用显式ADD
或MUL
指令的情况下将寄存器相加或相乘(或等效)。
由于每个人都以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等语言中使用指针,因此它是一个强大的指令。