接下来,让我们透过执行 AddNum 函数的源代码部分,来看一下参数的接收、返回值的返回等机制(代码清单 10-5)。
ebp 寄存器的值在(1)中人栈,在(5)中出栈。这主要是为了把函数中用到的 ebp 寄存器的内容,恢复到函数调用前的状态。在进入函数处理之前,无法确定 ebp 寄存器用到了什么地方,但由于函数内部也会用到 ebp 寄存器,所以就暂时将该值保存了起来。CPU 拥有的寄存器是有数量限制的。在函数调用前,调用源有可能已经在使用 ebp寄存器了。因而,在函数内部利用的寄存器,要尽量返回到函数调用前的状态。为此,我们就需要将其暂时保存在栈中,然后再在函数处理完毕之前出栈,使其返回到原来的状态。
(2)中把负责管理栈地址的 esp 寄存器的值赋值到了 ebp 寄存器中。这是因为,在 mov 指令中方括号内的参数,是不允许指定 esp 寄存器的。因此,这里就采用了不直接通过 esp,而是用 ebp 寄存器来读写栈内容的方法。
(3)是用[ebp+8] 指定栈中存储的第1 个参数 123,并将其读出到eax 寄存器中。像这样,不使用 pop 指令,也可以参照栈的内容。而之所以从多个寄存器中选择了 eax 寄存器,是因为eax 寄存器是负责运算的累加寄存器。
通过(4)的add 指令,把当前 eax 寄存器的值同第 2个参数相加后的结果存储在 eax 寄存器中。[ebp+12]是用来指定第2个参数 456的。在 C语言中,函数的返回值必须通过 eax 寄存器返回,这也是规定。不过,和 ebp 寄存器不同的是,eax 寄存器的值不用还原到原始状态。至此,我们进行了很多细节的说明,其实就是希望大家了解“函数的参数是通过栈来传递,返回值是通过寄存器来返回的”这一点。
(6)中 ret 指令运行后,函数返回目的地的内存地址会自动出栈据此,程序流程就会跳转返回到代码清单 10-4 的(6)( Call AddNum的下一行)。这时,AddNum 函数人口和出口处栈的状态变化,就如图10-5 所示。将图 10-4和图 10-5 按照(a)(b)(c)(d)(e)(f)的顺序来看的话,函数调用处理时栈的状态变化就会很清楚了。由于(a状态时处理跳转到 AddNum 函数,因此(a)和(b)是同样的。同理在(d)状态时,处理跳转到了调用源,因此(d)和(e)是同样的。在f)状态时则进行了清理处理。栈的最高位的数据地址,是一直存储在esp 寄存器中的。
标签:10,函数,状态,10.8,eax,ebp,寄存器,内部 From: https://www.cnblogs.com/z1218/p/17099682.html