Stack
函数生存在栈空间。其生长方向与堆空间的生长方向相反。
一次函数调用占用的一块连续空间,被称为栈帧stack frame,代表一个还未返回的函数调用。
Call Stack 由 Stack Frames 组成
void f(){ g(); }
void g(){ h(); }
void main(){ f(); }
+---------------+ 高地址
| main() 栈帧 |
+---------------+
| f() 栈帧 |
+---------------+
| g() 栈帧 |
+---------------+
| h() 栈帧 | <- 当前正在执行的函数
+---------------+
| |
| 未使用的栈 |
~ ~
| 未使用的堆 |
| |
| 已分配的堆 | <- 例如:new int[100];分配出去的内存
+---------------+
| 其它 | 低地址
现实中有很多这样的例子:两头掘进施工的山洞,施工快的一个分队掘进总距离就最长。
如果两队同向掘进,A队从起点,B队从1/2路程开始,则任何分队完成任务后都无法帮助另一个分队。
Calling Conventions
关于函数怎么调用,怎么返回,怎么传递参数,怎么使用栈帧的约定。
Activation Frame
当前活动帧位于栈顶部。它对应的函数h()执行时面对的一片自由的大海,剩余未使用栈空间全属于它。
意思就是当前函数h(),无需担心在执行代码分配栈空间时,会侵犯其它函数栈的地盘。
而g()帧,f()帧暂时被关押在栈的内部,他们的执行状态被保存在栈帧,等待恢复执行。
一般来说,只有活动帧中知道底层帧的变量指针或引用,才能修改非活动帧中的变量。
显然,底层帧的生存期大于上层的帧。当g()调用h()时,g()中变量的生存期长,h()变量的生存期短。
栈帧的范围
由%EBP和%ESP这两个寄存器确定范围
BP ( base pointer ) 寄存器,记录栈帧的位置,因此也称为Frame Pointer。
它的用途在于追踪和计算函数参数和局部变量等的位置。这些参数肯定不能用绝对地址,而要用相对地址。
相对地址的基础锚位就着落在%EBP值上。此寄存器的值是需要明确的指定和修改的,例如:mov %EBP, XXX
SP ( stack poinger ) 寄存器。
用于追踪帧顶位置,当活动函数需要分配局部变量等栈内存要求时,需要在%ESP基础上取。
很多指令会修改此寄存器,例如:PUSH 123; 等价于
sub %esp, 4 ; esp指针减去4字节,就是占用栈空间4字节
mov [%esp], 123 ; 把123拷贝到[esp]指向的内存
IP ( instruction pointer ) 寄存器。
存储了当前指令
参考:http://www.unixwiz.net/techtips/win32-callconv-asm.html