C语言函数调用栈的详细教程(10)
2023-04-29 来源:飞速影视
push
可选。如有必要,被调函数负责保存某些寄存器(íi/%esi/ëx)值
函数跋
(epilogue)
pop
可选。如有必要,被调函数负责恢复某些寄存器(íi/%esi/ëx)值
mov ëp, %esp*
恢复主调函数的栈顶指针%esp,将其指向被调函数栈底。此时,局部变量占用的栈空间被释放,但变量内容未被清除(跳过该处理)
pop ëp*
主调函数的帧基指针ëp出栈,即恢复主调函数栈底。此时,栈顶指针%esp指向主调函数栈顶(espesp-4),亦即返回地址存放处
ret
从栈顶弹出主调函数压在栈中的返回地址到指令指针寄存器%eip中,跳回主调函数该位置处继续执行。再由主调函数恢复到调用前的栈
*:这两条指令序列也可由leave指令实现,具体用哪种方式由编译器决定。
若主调函数和调函数均未使用局部变量寄存器EDI、ESI和EBX,则编译器无须在函数序中对其压栈,以便提高程序的执行效率。
参数压栈指令因编译器而异,如下两种压栈方式基本等效:
extern CdeclDemo(int w, int x, int y, intz); //调用CdeclDemo函数
CdeclDemo(1, 2, 3, 4); //调用CdeclDemo函数
压栈方式一
压栈方式二
pushl 4 //压入参数z
pushl 3 //压入参数y
pushl 2 //压入参数x
pushl 1 //压入参数w
call CdeclDemo //调用函数
addl $16, %esp //恢复ESP原值,使其指向调用前保存的返回地址
subl $16, %esp//多次调用仅执行一遍
movl $4, 12(%esp) //传送参数z至堆栈第四个位置
movl $3, 8(%esp) //传送参数y至堆栈第三个位置
movl $2, 4(%esp) //传送参数x至堆栈第二个位置
movl $1, (%esp) //传送参数w至堆栈栈顶
call CdeclDemo //调用函数
两种压栈方式均遵循C调用约定,但方式二中主调函数在调用返回后并未显式清理堆栈空间。因为在被调函数序阶段,编译器在栈顶为函数参数预先分配内存空间(sub指令)。函数参数被复制到栈中(而非压入栈中),并未修改栈顶指针,故调用返回时主调函数也无需修改栈顶指针。gcc3.4(或更高版本)编译器采用该技术将函数参数传递至栈上,相比栈顶指针随每次参数压栈而多次下移,一次性设置好栈顶指针更为高效。设想连续调用多个函数时,方式二仅需预先分配一次参数内存(大小足够容纳参数尺寸和最大的函数即可),后续调用无需每次都恢复栈顶指针。注意,函数被调用时,两种方式均使栈顶指针指向函数最左边的参数。本文不再区分两种压栈方式,"压栈"或"入栈"所提之处均按相应汇编代码理解,若无汇编则指方式二。
本站仅为学习交流之用,所有视频和图片均来自互联网收集而来,版权归原创者所有,本网站只提供web页面服务,并不提供资源存储,也不参与录制、上传
若本站收录的节目无意侵犯了贵司版权,请发邮件(我们会在3个工作日内删除侵权内容,谢谢。)
www.fs94.org-飞速影视 粤ICP备74369512号