主函数在调用函数过程中栈空间的使用情况,很清晰准确,多参照。主函数栈帧也是有限空间,下文实例是D8字节,主函数内部变量增多也会溢出,解决方法动态内存分配+智能指针,或者直接用容器类

主函数在调用函数过程中栈空间的使用情况

AHAOAHA 2018-01-15 20:22:47  3918  收藏 1
分类专栏: c语言
版权
#include<stdio.h>
int Add(int a,int b)
{
    int z=0;
    z=a+b;
    return z;
}
int main()
{
    int a=3;
    int b=5;
    int ret=0;
    ret=Add(a,b);
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
编写一段简单的代码用来理解主函数在调用函数过程中栈空间的使用情况。
将编码转为汇编代码,我们来一步一步分析。
在开始之前,首先要知道esp和ebp的作用:
•esp表示一个指针:该指针永远指向已经使用的栈空间的栈顶。
•ebp表示一个指针:该指针永远指向已经使用的栈空间的栈底。
(栈空间的使用是从高地址向低地址使用的)
•调用mainSRTStartup()函数
在进入主函数之前,会先调用mainSRTSartup()函数。
在mainSRTStartup()函数调用完成后,栈空间的使用情况如下:
•进入main()主函数

可以看到,在进入主函数之后,并没有直接创建变量。
push ebp —>压栈(将ebp的地址存入esp所指向位置的上面4个字节内)

move ebp esp—>让ebp指向esp所指向的位置

sub 0D8h—>让esp指向esp减去0D8h(十六进制数字)的位置

push ebx(寄存器)
push esi(寄存器)
push edi(寄存器)

lea edi,[ebp-0D8h]—>把[ebp-0D8h]的地址存入edi中
mov ecx,36h—>将36h(十六进制数字)存入ecx中
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
从edi这个位置开始,重复做一件事情(重复ecx(36h)次):将eax(0CCCCCCCCh)的内容存储到edi中存储的地址([ebp-0D8h])开始的向上的位置里去。—(初始化内存空间)

•开始执行主函数语句

上图汇编代码为vs2013环境下的汇编代码,以下讲解使用vc6.0的环境下的编译代码进行讲解。

 int a=3;
 int b=5;
 int ret=0;
1
2
3
mov dword[ebp-4],3—>将3存入[ebp-4]所指向的内存空间内
mov dword[ebp-8],5—>将5存入[ebp-8]所指向的内存空间内
mov dword[ebp-12],0—>将0存入[ebp-12]所指向的内存空间内

•调用Add()函数
mov eax,dword ptr[ebp-8]—>将[ebp-8]中放的内容存入eax中(寄存器),此时[ebp-8]中的内容为5
push eax—>在栈顶开辟新的空间(4个字节)来存放5
mov ecx,dword ptr[ebp-4]—>将[ebp-4]中放的内容存入ecx中(寄存器),此时[ebp-4]中的内容为3
push ecx—>在栈顶开辟新的空间(4个字节)来存放3

call @ILT+5(_Add)(013B11EFh)—>函数调用
从013B1B04这个位置直接跳转到013B11EFh所指向的位置(在操作系统中,此过程被称为“现场保护”)此时main()函数并没有结束,只是停在了013B1B04这个位置,等待Add()函数执行完毕之后,main()函数会从013B1B04这个位置继续向下执行。此时在栈顶又会开辟新的空间(4字节),用来存放013B1B04这个地址。


jmp Add(013B11EFh)—>跳入Add()函数的内部

开始执行函数语句之前,Add()函数做了与main()函数一样的工作。
※所以推断出在每一次函数调用开始的时候都要为这个函数开辟一段空间(运行时堆栈/函数栈帧)。
此时的内存使用情况为:

底部绿色的ebp为main()函数的栈底地址,是因为当一个函数被调用完毕时必须回到上一个函数中去,所以就要保存上一个函数的栈底地址。
•开始执行Add()函数语句

int z=0;
z=a+b;
return z;
1
2
3
所对应的汇编代码如图所示:

mov dword ptr[ebp-4],0—>将0存入[ebp-4]的位置
mov eax,dword ptr[ebp+8]—>将ebp+8中的内容存入eax(eax此时存储的是b的值5)中
add eax,dword ptr[ebp+0Ch]—>将a与b的值相加并存放在eax中
mov dword ptr[ebp-4],eax—>将此刻eax的值(8)放到[ebp-4]的位置里去
mov eax,dword ptr[ebp-4]—>又把[ebp-4]中的值放到eax中去,因为当函数调用完成时,所使用的内存会返还给电脑,但是寄存器不会。
pop edi
pop esi
pop ebx
上面三句汇编代码会令esp的位置返回,此时esp指向的位置如图所示:

即图片中紫色esp所指向的位置。
mov esp,ebp—>把ebp的值赋给给esp(令esp返回)。如图所示:

pop ebp—>出栈,将原来栈中的元素放入上一个函数的ebp中,(令ebp指向的位置回到上一个函数的栈底),此时esp也会因为ebp的出栈而返回四个字节。如图所示:

即为图中紫色ebp所指向的位置。
函数执行到这一步时,图中紫色esp向上的空间(即为为Add()函数所开辟的空间)已经返还给了操作系统。
ret —>直接跳回call指令的下一条指令(是由紫色esp向下四个字节所保存的地址(013B1B04)找到call指令的下一条指令),此时esp返回四个字节,保存地址(013B1B04)的四个字节也被返还给操作系统。此时的esp和ebp所指向的位置如图所示:

即为图中紫色esp与紫色ebp所指向的位置。
接下来的汇编代码如图所示:

add esp,8—>给esp加8,让esp返回8个字节,如图所示:

易知,紫色esp以上的位置都会被返还给操作系统,所以此时为Add()函数创建的形参不可以再继续使用。
mov dword ptr[ret],eax—>将eax中的值(8)保存到ret所在的4个字节中去。

xor eax,eax—>将eax清零。
————————————————
版权声明:本文为CSDN博主「AHAOAHA」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/h___q/article/details/79038327

VS2008环境下主函数的栈帧是E4(228)字节,初始化39H(57)次,每次4个字节,填充满E4(228)字节上文中是D8字节,初始化36H(54)次,每次4个字节,填充满D8(216)字节

c++ - 数组声明在main函数中运行时栈溢出?

数组声明在main函数中运行时栈溢出?

发表于2017 年 3 月 29 日 作者 admin

环境是win10+VS2015

1

int common_subsequence[1000][1000]

声明在mian函数中,运行到这步栈溢出;

1

int common_subsequence[100][100]

声明在main函数中,不会发生栈溢出;

1

int common_subsequence[1000][1000]

声明为全局,不会发生栈溢出。
究竟怎么回事?有什么建议吗

 

windows下栈的大小默认是1mb,

1

4*1000*1000

约为4mb,所以调用main会发生栈溢出。

common_subsequence是自动变量,对象的内存分配会在函数开始前完成,也就是说溢出发生在main函数第一行代码执行之前。

建议用动态内存分配+智能指针,或者直接用容器类。

 

1
2

std::unique_ptr&lt;int [][1000]&gt; sequence(new int[1000][1000]);
std::vector&lt;std::vector&lt;int&gt; &gt; sequence;

 

 

解决方案

 

windows下栈的大小默认是1mb,4*1000*1000约为4mb,所以调用main会发生栈溢出。

common_subsequence是自动变量,对象的内存分配会在函数开始前完成,也就是说溢出发生在main函数第一行代码执行之前。

建议用动态内存分配+智能指针,或者直接用容器类。

std::unique_ptr<int [][1000]> sequence(new int[1000][1000]);
std::vector<std::vector<int> > sequence;

http://www.caotama.com/96747.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值