函数调用堆栈详细过程

以下面的程序为例:

#include<iostream>

using namespace std;

int  sum(int a,int b)
{
	int temp=0;
	temp=a+b;
	return temp;

}
int main()
{
	int a=10;
	int b=20;
	int ret=sum(a,b);
	cout<<ret<<endl;
	return 0;
}

问题1:main函数调用sum,sum执行完后,怎么知道回到哪个函数中?
问题2:sum函数执行完,回到main后,怎么知道从哪行指令开始运行。

要描述一个栈,需要知道栈底和栈顶就可以了。其中esp来标识栈顶,ebp来标识栈底。(栈是从高地址向低地址增长的,所以ebp要比esp的地址大)

1)假设mian函数的栈帧是:

…esp
ret
b 10
a 20
…ebp0–0x0018ff40

2)然后就是对sum函数的参数进行压栈:

…esp
10=》int a
20=》int b
ret
b 10
a 20
…ebp0–0x0018ff40

3)需要注意的是sum的参数的压栈顺序是从有向左压栈。

上述压栈完毕后,进行call sum,call sum做的第一件事就是,将call的下一行指令的地址进行压栈(解决了上面的问题二)!然后第二件事情才是进入sum。这里假设call指令的下一行代码的地址是0x08124458。过程如下:

…esp
0x08124458
10=》int a
20=》int b
ret
b 10
a 20
…ebp0–0x0018ff40

4)进入sum函数后,在执行sum中的代码之前,要进行一些前处理:就是要将mian函数的栈帧即main的ebp压栈,然后开辟新的栈也就是sum的栈:

…esp

…ebp
0x0018ff40
0x08124458
10=》int a
20=》int b
ret
b 10
a 20

5)然后开始执行sum中的代码:

需要注意的是,temp=a+b的这一行代码,汇编指令中会有特殊的地址偏移,在栈上找到a和b的值。然后return temp这一行代码,是将temp的值放在了全局的eax寄存器上。
只有定义局部变量或者函数调用的时候才会进行压栈操作,其余的计算过程和return操作是不会在栈上体现的。
…esp

temp 0
…ebp
0x0018ff40
0x08124458
10=》int a
20=》int b
ret
b 10
a 20

6)执行完sum后,会进行栈帧的回退,首先,将esp的位置放到ebp的位置,然后执行栈的pop,pop出的地址值就是mian函数的ebp!将这个值赋给ebp,至此,就回到了mian的栈帧中:

…esp
0x08124458
10=》int a
20=》int b
ret
b 10
a 20
…ebp–0x0018ff40

7)然后开始继续运行,首先要找到mian中继续运行执行,方式就是执行一次栈的pop,将这个地址放入CPU的PC寄存器中。完毕后,栈的内容大概是:

…esp
10=》int a
20=》int b
ret
b 10
a 20
…ebp–0x0018ff40

8)然后程序开始从0x08124458开始执行,0x08124458中往往会有一个 add esp ,8 ,然后再是其他的 。这就会把esp加8个字节,也就是出栈两次,将栈中压入的sum的参数出栈,此时:

…esp
ret
b 10
a 20
…ebp–0x0018ff40

9)之后就交给指令了,这些指令会将eax寄存器的值赋给ret。完成sum函数的调用!!

…esp
ret 30
b 10
a 20
…ebp–0x0018ff40

下面回答问题1:问题1:main函数调用sum,sum执行完后,怎么知道回到哪个函数中?
就是通过压栈的顺序。

(未完待续,见视频)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值