我还是对LEA指令充满了不解和困惑……:)

totkid 2001-09-02 04:06:15
加精
我最大的疑问就是,lea是不是没用?
书上说:lea是用来把指定地址处的操作数的偏移,输送到指定的寄存器
可是,我们在“指定地址”的时候,不就已经得到了偏移吗??
既然如此,只要把这个偏移mov到该寄存器就可以了呀?


假设ppp是一个存储器操作数,偏移为3000H
例如:
lea bx,ppp
实际上替换成
lea bx,[3000H]
机器码是
8D1E0030

等效的mov指令
mov bx,offset ppp
实际上替换为
mov bx,3000H
BB0030

可以节省一个字节

诸如偏移这种东西,都是编译器在编译期就已经算好的,既然如此,可以把它们看成
“立即数”了,要把一个立即数送到一个寄存器,有现成的mov可以用呀

记得有个朋友说过局部变量应该用lea,可是我想,局部变量的地址难道不是在编译期算好的吗?既然算好的那不是立即数吗?既然要把立即数送到寄存器那难道不可以用mov吗?

而且,我的另一个疑问是:lea到底干了什么?
从DEBUG的反汇编可以看出明显的区别
lea bx,[3000]
mov bx, 3000
一个是直接寻址,一个是立即寻址

我想,难不成LEA是访问了一遍存储器,然后计算出它的偏移,然后再把偏移储存到寄存器的?
可是,这个偏移是个编译期常数呀,用得着“计算”吗??
而且,运行的时候,又怎么能确定它在哪个段呢??段是可以重叠的


我十分不解,请帮忙,谢谢!:)
...全文
1179 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
蚊子王 2001-09-04
  • 打赏
  • 举报
回复
test
vBin 2001-09-03
  • 打赏
  • 举报
回复
呵呵,团结的力量真大呀。好感动!
totkid 2001-09-03
  • 打赏
  • 举报
回复
万分感谢!!!!!!!!
蚊子王 2001-09-03
  • 打赏
  • 举报
回复
关于局部变量我还是举个例子吧:
比如以下的C/C++函数(c调用方式):
void abc(void){
int a=0x1234;
efg(&a);
}
翻译成对等的ASM代码:
_abc proc near
push ebp
mov ebp,esp
sub esp,4h
;
lea eax,[ebp-4h]
mov [eax],1234h
push eax
call _efg
add esp,4h
;
mov esp,ebp
pop ebp
ret
_abc endp
这时栈的情况如下:
---------- <----esp
a
---------- <----ebp
old ebp
----------
ret address
----------
这时要取得a的地址,那只能用"lea eax,[ebp-4h]"而不能用“mov offset...”了。
CNer 2001-09-03
  • 打赏
  • 举报
回复
我补充一点:在tasm中,lea指令,很多都是替换成等效的mov offset指令的。
azuo_lee 2001-09-03
  • 打赏
  • 举报
回复
堆栈种分配的局部变量所谓的“标号”,你以为是什么?(都是那些该死的宏惹的祸,大家要都是老老实实写代码,就不会有这些疑问了)。
比如你用local在栈上定义了一个局部变量LocalVar,你知道实际的指令是什么么?一般都差不多像下面的样子:
push ebp
mov esp, ebp
sub esp, 4
现在栈上就有了4各字节的空间,这就是你的局部变量。
接下来,你执行mov LocalVar, 4,那么实际的指令又是什么?是这样:
mov dword ptr [ebp-4], 4
于是,这个局部变量的“地址”就是ebp-4——显然,它不是一个固定的地址。现在需要将它的“地址”作为参数传给某个函数,你这样写:
invoke/call SomeFunc, addr LocalVar
实际生成的指令是:
lea eax, [ebp-4]
push eax
call SomeFunc
当然,你也可以写成:
mov eax, ebp
sub eax, 4
push eax
call SomeFunc
看到了,这里多了一条指令。这就是lea的好处。于是,lea又多了一个非常美妙的用途:作简单的算术计算,特别是有了32位指令的增强寻址方式,更是“如虎添翼”:
比如你要算EAX*4+EBX+3,结果放入EDX,怎么办?
mov edx, eax
shl edx, 2
add edx, ebx
add edx, 3
现在用lea一条指令搞定:
lea edx, [ebx+eax*4+3]
totkid 2001-09-03
  • 打赏
  • 举报
回复
对不起……我还是有一些不解
局部变量?
如果我要
lea eax,LocalVar
那么我是通过什么寻址方式找到LocalVar的呢???
既然能通过寻址方式找到该变量
为什么不直接把“地址”送到该寄存器呢??
还有……lea指令能够在运行时确定标号的有效地址,请问这是什么意思呢?
我记得标号都是在编译期就被转化为某个确定的偏移了,然后连接时期确定段值
这一切都应该是在运行期之前就确定的东西呀!

还有,关于在堆栈中运行时分配的变量,如果我们为它设定了一个标号,那么我们
要lea它的地址的时候,是怎么知道我们要lea的是某个地址的变量呢?既然我们若
可以指定——“就lea这个变量的地址给我看”,那么我们起码要知道到底是哪个变量,
那么我们到底怎么指定呢??标号是根本不存在于运行时的程序中的,那么我们肯定应该是
指定它的地址了,既然能指定它的地址……那么直接把它的地址发到某个寄存器不就得了?

cui的那个例子使我开了一些窍,不过,难道lea只是用在这些窍门上吗?

我太苯了!不好意思!
thickhead_cat 2001-09-03
  • 打赏
  • 举报
回复
我不同意楼上的说法:

其实,
1、汇编里有很多功能,能用不同的指令实现,这一点也不奇怪
2、lea是cpu的指令,而mov ax,offset xxx 只是我们利用编译器的offset自动计算的功能
3、lea指令远在exe文件产生前就有了,那时顶多只有com文件,所以它和执行时计算偏移量无关的!
蚊子王 2001-09-02
  • 打赏
  • 举报
回复
vBin(彬)说的很对,这是用lea(addr事实上就是lea语句)的一个理由;另一个理由是:
假设要bx+10h->bx但又不想影响flag那就只能用lea bx,[bx+10h]了。 
vBin 2001-09-02
  • 打赏
  • 举报
回复
addr可以处理局部变量而 offset 则不能。局部变量只是在运行时在堆栈中分配内存空间。而 offset 则是在编译时由编译器解释,这显然不能用offset 在运行时来分配内存空间。编译器对 addr 的处理是先检查处理的是全局还是局部变量,若是全局变量则把其地址放到目标文件中,这一点和 offset 相同,若是局部变量,就在执行 invoke 语句前产生如下指令序列:
lea eax, LocalVar
push eax
因为lea指令能够在运行时决定标号的有效地址,所以有了上述指令序列,就可以保证 invoke 的正确执行了。

21,458

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧