C语言学习笔记—P17(函数栈帧的创建与销毁<超详解版>+图解+题例)

目录

前言:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                      ——By 作者:新晓·故知

那些代码背后的故事:

通过反汇编讲解函数栈帧的创建与销毁!

 注:本次编译环境为:Visual Stdio 2013 !

 越高级的编译器越难以抽离函数栈帧分装的过程!

main函数也会被其他函数调用哦!

                                                                 

                                                                    

反汇编查看:​

 为a、b、c开辟空间并存值:​

                                                           ​

                                                                     ​

                                                                      ——By 作者:新晓·故知  整理+创作​

                                                                    ​

完成a的开辟、存值:

 完成b的开辟、存值:​​

 完成c的开辟、存值:​

                                                                     ​

 push 压栈:

                                                                     ​

 传参:​

 call指令将下一条指令的地址拿出压栈!​

                                                                   ​

 为Add函数准备栈帧!​

                                                                      ——By 作者:新晓·故知  整理+创作​

                                              

形参对应的函数(子函数)计算:​

 注:

函数调用时没有创建形参,最初在调用函数时,通过call指令和其他指令,就将参数传过去了,将实参a、b通过push压栈在a'、b'。因为是在栈中进行,所以先传的是b(右面的参数),压入栈中,再传a(左面的参数),故参数从右向左传参!

当进入函数计算时,形参回头去找压栈时的空间的a、b对应的值!

因此说:形参是对实参的临时拷贝!改变形参的值不影响实参!

 返回:

 先在eax保存z的值,安全!

                                                            ​

                                                            ​

此时所有指令回到main函数!main函数的栈帧交给esp,ebp进行维护!Add函数的栈帧销毁!别担心!z的值在寄存器eax中存放,安全着呢!

                                                             ​

 这时显现出最初在栈顶存储call指令的下一条指令地址的作用:

在进行函数调用结束后回到主函数,并从call指令的下一条指令开始执行!

逻辑严谨!不仅要走的出去,也要回的来!

 esp执行至此,形参的空间均已释放归还给操作系统!

 而eax的值暂存的z=30!主函数进行打印输出!

解答:

1.局部变量的创建是首先为函数分配栈帧空间,栈帧空间初始化一部分空间后,再为局部变量分配一些空间!

2.局部变量不初始化的是随机值,因为创建的时候是随机放置的值!初始化后随机值被覆盖!

3.函数的传参是在未调用形参对应的函数(子函数)时通过push 操作将实参的值压栈,当真正进入形参函数时,在形参对应的函数(子函数)栈帧里通过指针的偏移量,找回形参,进行使用!

传参是从右向左传!

4.形参是在压栈时开辟的空间 ,它和实参只是在数值上相同的,空间上是独立的!

形参是实参的一份临时拷贝!改变形参的值不影响实参!

5.函数的调用见以上讲解!

6.函数调用的结果的返回:

调用之前将call指令的下一条指令的地址压入栈,记录存储,将ebp的调用的函数的上一个函数(此例为主函数ebp-main)的栈帧的ebp地址存储记录,当形参对应的函数(子函数)调用完返回时,弹出ebp就找到原始函数(上一个函数,此例为main函数)的ebp,而esp的指针移动返回时就能找到原始函数(此例为main函数)的栈帧的顶,回到原始函数(main函数)的栈帧空间,就可以跳转到已记录的call指令的下一条指令的地址,进行返回。

返回值是通过寄存器eax带回!

                                                              

注:

函数内部创建的静态变量是在全局变量空间开辟的!而以上介绍的是在栈区空间开辟的!

编译器会根据函数的不同,开辟合适的空间!

 1.a和b是不连续的,相隔的空间大小取决于不同的编译器!

2.函数的形参可以理解为:不在形参对应的函数(子函数)的栈帧里,而在主函数main拓展的栈帧里!

                                                                         ——Since 作者:新晓·故知  整理+创作



前言:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                      ——By 作者:新晓·故知

那些代码背后的故事:

通过反汇编讲解函数栈帧的创建与销毁!

 注:本次编译环境为:Visual Stdio 2013 !

VS2013运行结果会一闪而过
解决办法1:
设置项目属性
解决办法2:
system("pause");

 

 越高级的编译器越难以抽离函数栈帧分装的过程!

int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	c = Add(a, b);
	return 0;
}

main函数也会被其他函数调用哦!

                                                                 

 

 

 

 

  

                                                                    

反汇编查看:

 

 为a、b、c开辟空间并存值:

 

                                                           

 

 

 

                                                                     

 

 

                                                                      ——By 作者:新晓·故知  整理+创作​​​​​​​

 

 

 

 

   

                                                                    

 

完成a的开辟、存值:

 

 完成b的开辟、存值:

 完成c的开辟、存值:

 

                                                                     

 

 push 压栈:

 

 

  

                                                                     ​​​​​​​

 传参:

 

 call指令将下一条指令的地址拿出压栈!

 

                                                                   

 为Add函数准备栈帧!

 

                                                                      ——By 作者:新晓·故知  整理+创作​​​​​​​

 33h次:16进制的33h次

 

                                              

形参对应的函数(子函数)计算:

 

 

 注:

函数调用时没有创建形参,最初在调用函数时,通过call指令和其他指令,就将参数传过去了,将实参a、b通过push压栈在a'、b'。因为是在栈中进行,所以先传的是b(右面的参数),压入栈中,再传a(左面的参数),故参数从右向左传参!

当进入函数计算时,形参回头去找压栈时的空间的a、b对应的值!

因此说:形参是对实参的临时拷贝!改变形参的值不影响实参!

 

 返回:

 先在eax保存z的值,安全!

 

                                                            

 

 

                                                            

此时所有指令回到main函数!main函数的栈帧交给esp,ebp进行维护!Add函数的栈帧销毁!别担心!z的值在寄存器eax中存放,安全着呢!

                                                             

 这时显现出最初在栈顶存储call指令的下一条指令地址的作用:

在进行函数调用结束后回到主函数,并从call指令的下一条指令开始执行!

逻辑严谨!不仅要走的出去,也要回的来!

 esp执行至此,形参的空间均已释放归还给操作系统!

 

 而eax的值暂存的z=30!主函数进行打印输出!

解答:

1.局部变量的创建是首先为函数分配栈帧空间,栈帧空间初始化一部分空间后,再为局部变量分配一些空间!

2.局部变量不初始化的是随机值,因为创建的时候是随机放置的值!初始化后随机值被覆盖!

3.函数的传参是在未调用形参对应的函数(子函数)时通过push 操作将实参的值压栈,当真正进入形参函数时,在形参对应的函数(子函数)栈帧里通过指针的偏移量,找回形参,进行使用!

传参是从右向左传!

4.形参是在压栈时开辟的空间 ,它和实参只是在数值上相同的,空间上是独立的!

形参是实参的一份临时拷贝!改变形参的值不影响实参!

5.函数的调用见以上讲解!

6.函数调用的结果的返回:

调用之前将call指令的下一条指令的地址压入栈,记录存储,将ebp的调用的函数的上一个函数(此例为主函数ebp-main)的栈帧的ebp地址存储记录,当形参对应的函数(子函数)调用完返回时,弹出ebp就找到原始函数(上一个函数,此例为main函数)的ebp,而esp的指针移动返回时就能找到原始函数(此例为main函数)的栈帧的顶,回到原始函数(main函数)的栈帧空间,就可以跳转到已记录的call指令的下一条指令的地址,进行返回。

返回值是通过寄存器eax带回!

                                                              

注:

函数内部创建的静态变量是在全局变量空间开辟的!而以上介绍的是在栈区空间开辟的!

编译器会根据函数的不同,开辟合适的空间!

 1.a和b是不连续的,相隔的空间大小取决于不同的编译器!

2.函数的形参可以理解为:不在形参对应的函数(子函数)的栈帧里,而在主函数main拓展的栈帧里!

                                                                         ——Since 作者:新晓·故知  整理+创作

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值