为什么局部变量只能在定义该变量的函数内进行参阅呢?这是因为,局部变量是临时保存在寄存器和栈中的。正如本章前半部分讲的那样,函数内部利用的栈,在函数处理完毕后会恢复到初始状态,因此局部变量的值也就被销毁了,而寄存器也可能会被用于其他目的。因此,局部变量只是在函数处理运行期间临时存储在寄存器和栈上。
在代码清单 10-6 中定义了 10个局部变量。这是为了表示存储局部变量的不仅仅是栈,还有寄存器。为确保 cl~c10 所需的领域,寄存器空闲时就使用寄存器,寄存器空间不足的话就使用栈。
下面让我们来看一下代码清单 10-7 中 TEXT 段定义的内容。(7)表示的是 MyFunc 函数的范围。在 MyFunc 函数中定义的局部变量所需要的内存领域,会被尽可能地分配在寄存器中。大家可能会认为用高性能的寄存器来代替普通的内存是很奢侈的事情,不过编译器不会这么认为、只要寄存器有空间,编译器就会使用它。因为与内存相比使用寄存器时访问速度会高很多,这样就可以更快速地进行处理。局部变量利用寄存器,是 Borland C++ 编译器最优化的运行结果。旧的编译器没有类似的最优化功能,局部变量就可能会仅仅使用栈。
代码清单中的(8)表示的是往寄存器中分配局部变量的部分。仅仅对局部变量进行定义是不够的,只有在给局部变量赋值时,才会被分配到寄存器的内存区域。(8)就相当于给 5 个局部变量 c1~c5 分别赋予数值1~5这一处理。eax、edx、ecx、ebx、esi是 Pentium 等x86系列 32位CPU 寄存器的名称(参考表 10-2)。至于使用哪一个寄存器则要由编译器来决定。这种情况下,寄存器只是被单纯地用于存储变量的值,和其本身的角色没有任何关系。
x86 系列 CPU 拥有的寄存器中,程序可以操作的有十几个。其中空闲的,最多也只有几个。因而,局部变量数目很多的时候,可分配的寄存器就不够了。这种情况下,局部变量就会申请分配栈的内存空间。虽然栈的内存空间也是作为一种存储数据的段定义来处理的,但在程序各部分都可以共享并临时使用这一点上,它和 DATA 段定义及BSS 段定义在性质上还是有些差异的。例如,在函数入口处为变量申请分配栈的内存空间的话,就必须在函数出口处进行释放。否则,经过多次调用函数后,栈的内存空间就会被用光了。
在(8)这一部分中,给局部变量 c1~c5 分配完寄存器后,可用的寄存器数量就不足了。于是,剩下的 5 个局部变量 c6~c10 就被分配了栈的内存空间,如(9)所示。函数入口(10)处的 add esp,-20 指的是对栈数据存储位置的 esp 寄存器(栈指针)的值做减 20 的处理。为了确保内部变量 c6~c10 在栈中,就需要保留 5个int类型的局部变量(4字节 x5 = 20字节)所需的空间。(11)中的 mov ebp,esp 这一处理指的是把当前 esp 寄存器的值复制到 ebp 寄存器中。之所以需要(11)这一处理,是为了通过在函数出口处的(12)这一 move esp,ebp 的处理,把 esp 寄存器的值还原到原始状态,从而对申请分配的栈空间进行释放,这时栈中用到的局部变量就消失了。这也是栈的清理处理。在使用寄存器的情况下,局部变量则会在寄存器被用于其他用途时自动消失(图 10-6)。
(9)中的 5 行代码是往栈空间中代人数值的部分。由于在向栈申请内存空间前,借助 mov ebp,esp 这个处理,esp 寄存器的值被保存到了 ebp 寄存器中,因此,通过使用[ebp - 4]、[ebp - 8]、[ebp - 12]、[ebp - 16]、[ebp - 2] 这样的形式,就可以将申请分配的 20 字节的栈内存空间切分成5个长度分别是4字节的空间来使用(图 10-7)。例如(9)中的 mov dword ptr [ebp - 4],6 表示的就是,从申请分配的内存空间的下端(ebp 寄存器指示的位置)开始往前4字节的地址([ebp - 4]中,存储着6这一4字节的数据。
关于往全局变量中代人局部变量的数值这一内容,这里不再进行说明。这时可能有读者会产生疑问,既然不进行说明,那为什么代码清单 10-6 中没有省略掉该部分呢?这是为了避免编译器的最优化功能如果仅进行定义局部变量并代人数值这一处理的话,编译器的最优化功能就会启动,届时编译器就会认为某些代码没有意义,从而导致汇编语言的源代码无法生成。
标签:esp,局部变量,内存空间,编译器,ebp,寄存器,10.10 From: https://www.cnblogs.com/z1218/p/17099723.html