我们知道,局部变量是临时保存在寄存器和栈中的。函数内部利用栈进行局部变量的存储,函数调用完成后,局部变量值被销毁,但是寄存器可能用于其他目的。所以,局部变量只是函数在处理期间临时存储在寄存器和栈中的
回想一下上一篇博客是不是定义了10个局部变量?这是为了表示存储局部变量的不仅仅是栈,还有寄存器。为了确保c1-c10所需的域,寄存器空闲的时候就会使用寄存器,寄存器空间不足的时候就会使用栈
让我们继续来分析上面代码的内容。_TEXT 段定义表示的是 MyFunc 函数的范围。在MyFunc 函数中定义的局部变量所需要的内存领域。会被尽可能的分配在寄存器中。大家可能认为使用高性能的寄存器来替代普通的内存是一种资源浪费,但是编译器不这么认为,只要寄存器有空间,编译器就会使用它。由于寄存器的访问速度远高于内存,所以直接访问寄存器能够高效的处理。局部变量使用寄存器,是Borland C++编译器最优化的运行结果
代码清单中的如下内容表示的是向寄存器中分配局部变量的部分
mov eax,1 mov eax,2 mov eax,3 mov eax,4 mov eax,5
仅仅对局部变量进行定义是不够的,只有在给局部变量赋值时,才会被分配到寄存器的内存区域。上述代码相当于就是给5个局部变量c1-c5分别赋值为1-5.eax、edx、ecx、ebx、esi是x86系列32位CPU寄存器的名称。至于使用哪个寄存器,是由 编译器 来决定的
x86系列CPU拥有的寄存器中,程序可以操作的是十几,其中空闲的最多会有几个。因而,局部变量超过寄存器数量的时候,可分配的寄存器就不够用了,这种情况下,编译器就会把栈派上用场,用来存储剩余的局部变量
在上述代码这一部分,给局部变量c1-c5分配完寄存器后,可用的寄存器数量就不足了。于是,剩下的5个局部变量c6-c10就被分配给了栈的内存空间。如下面代码所示:
mov dword ptr [ebp-4],6 mov dword ptr [ebp-8],7 mov dword ptr [ebp-12],8 mov dword ptr [ebp-18],9 mov dword ptr [ebp-20],10
函数入口 add esp,-20 指的是,对栈数据存储位置的esp 寄存器(栈指针)的值做减20的处理。为了确保内存变量c6-c10在栈中,就需要保留5个int类型的局部变量(4字节*5=20字节)所需的空间。mov ebp,esp 这行指令表示的意思是将esp 寄存器的值赋值到ebp 寄存器。之所以需要这么处理,是为了通过在函数出口处 mov esp ebp 这一处理,把esp 寄存器的值还原到原始状态,从而对申请分配的栈空间进行释放,这时栈中用到的局部变量就消失了。这也是栈的清理处理。在使用寄存器的情况下,局部变量则会在寄存器被用于其他用途时自动消失,如下图所示:
用于局部变量的栈空间的申请分配和释放:
mov dword ptr [ebp-4],6 mov dword ptr [ebp-8],7 mov dword ptr [ebp-12],8 mov dword ptr [ebp-18],9 mov dword ptr [ebp-20],10
这五行代码是往栈空间代入数值的部分,由于在向栈申请内存空间前,借助了 mov,ebp,esp 这个处理,esp寄存器的值被保存到了esp寄存器中,因此,通过使用[ebp-4]、[ebp-8]、[ebp-12]、[ebp-16]、[ebp-20]这样的形式,就可以申请分配20字节的栈内空间切分5个长度为4字节的空间来使用。例如、mov dword ptr [ebp-4],6 表示的就是,从申请分配的内存空间的下端(ebp寄存器指示的位置)开始向前4个字节的地址([ebp-4])中,存储着6这一字节数据
将栈内存空间进行分割: