使用MASM09
让编程改变世界
Change the world by program
局部变量的使用
[codesyntax lang="asm"]TestProc proc ;名为TestProc的子程序 local @loc1:dword, @loc2:word local @loc3:byte ;用local语句定义了3个变量 mov eax, @loc1 ;对应类型进行存储,然后返回 mov ax, @loc2 mov al, @loc3 retTestProc endp[/codesyntax] 我们来看看它反汇编之后是什么样子的: [codesyntax lang="asm"]
:00401000 55 push ebp:00401001 8BEC mov ebp, esp:00401003 83C4F8 add esp, FFFFFFF8:00401006 8B45FC mov eax, dword ptr [ebp-04]:00401009 668B45FA mov ax, word ptr [ebp-06]:0040100D 8A45F9 mov al, byte ptr [ebp-07]:00401010 C9 leave:00401011 C3 ret[/codesyntax] 可以看到,反汇编后的指令比源程序多了前后两段指令,它们是: [codesyntax lang="asm"]
:00401000 55 push ebp:00401001 8BEC mov ebp, esp:00401003 83C4F8 add esp, FFFFFFF8:00401010 C9 leave[/codesyntax] 这些就是使用局部变量所必需的指令,分别用于局部变量的准备工作和扫尾工作。 【分析过程】 当调用者执行了call TestProc指令后,CPU把返回的地址(当前地址)压入堆栈,再转移(jmp)到子程序执行。 esp在程序的执行过程中可能随时用到,不可能用esp来随时存取局部变量,ebp寄存器(可以理解为小三)是以堆栈段为默认数据段的,所以,可以用ebp做指针指向堆栈替代esp。 于是,在初始化前,先用一句push ebp指令把原来的dbp保存起来,然后把esp的值放到ebp中。 (介绍局部变量怎么腾出空间的)再后面就是堆栈中预留空间了,由于堆栈是向下增长的。所以要在esp中加一个负值,FFFFFFF8就是-8。 我们来考虑另一个问题:一个dword加一个word加一个byte不是7吗,为什么刚刚我们在堆栈为局部变量让出了8个字节的空间呢? 这是因为在80386处理器中,以dword (32位)为界对齐时存取内存速度最快,所以MASM宁可浪费一个字节,执行了这3句指令后,初始化完成,就可以进行正常的操作了,从指令中可以看出局部变量在堆栈中的位置排列。 在程序退出的时候,必须把正确的esp设置回去,否则,ret指令会从堆栈中取出错误的地址返回,看程序可以发现,ebp就是正确的esp值。 因为子程序开始的时候已经有一句mov ebp,esp,所以要返回的时候只要先mov esp,ebp,然后再pop ebp,堆栈就是正确的了。 在80386指令集中有一条指令可以在一句中实现这些功能,就是leave指令,所以,编译器在ret指令之前只使用了一句leave指令。 明白了局部变量使用的原理,就很容易理解使用时的注意点:ebp寄存器是关键(第一次听说小三是关键) 因为它起到保存原始esp的作用,并随时用做存取局部变量的指针基址,所以在任何时刻,不要尝试把ebp用于别的用途,否则会带来意想不到的后果。(在任何时候不要做对不起小三的事情,不然后果很严重 T_T)