堆栈溢出,怎样vc 修改堆栈大小小

:转载时请以超链接形式标明文章原始出处和作者信息及本声明
栈溢出(stackoverflow)的原因及解决办法作者:不要以为你赢了最近在做一个程序(VC6.0),功能大概有网络通信、数据库、绘图等。测试的时候程序一运行到某个函数就出现此错误,查了很多地方,试了很多解决办法,终于把问题解决了,写个日志提醒一下自己,也希望作为一个普遍解决办法让大家少费工夫(其他编译器也会出现同样的问题)。大家都知道,Windows程序的内存机制大概是这样的,全局变量(局部的静态变量本质也属于此范围)存储于堆内存,该段内存较大,一般不会溢出;函数地址、函数参数、局部变量等信息存储于栈内存,VC6中栈内存默认大小为1M,对于当前日益扩大的程序规模而言,稍有不慎就可能出问题。(动态申请的内存即new出来的内存不在栈中)即如果函数这样写:voidtest_stack_overflow(){char*chdata=new[2*];delete[]}是不会出现这个错误的,而这样写则不行:voidtest_stack_overflow(){charchdata[2*];}大多数情况下都会出现内存溢出的错误,不信在vc6中随便做个程序,调用一下这个函数试式。出现栈内存溢出的常见原因有2个:1&函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈。2&局部静态变量体积太大第一种情况不太常见,因为很多情况下我们都用其他方法来代替递归调用(反正我是这么做的),所以只要不出现无限制的调用都应该是没有问题的,起码深度几十层我想是没问题的,这个我没试过但我想没有谁会把调用深度作那么多。检查是否是此原因的方法为,在引起溢出的那个函数处设一个断点,然后执行程序使其停在断点处,然后按下快捷键Alt+7调出callstack窗口,在窗口中可以看到函数调用的层次关系。第二种情况比较常见了,我就是犯了这个错误,我在函数里定义了一个局部变量,是一个类对象,该类中有一个大数组,大概是1.5M。解决办法大致说来也有两种:1&增加栈内存的数目2&使用堆内存增加栈内存方法如下,在vc6种依次选择Project-&Setting-&Link,在Category中选择output,在Reserve中输入16进制的栈内存大小如:0x,然后点ok就可以了。其他编译器也有类似的设置,个人认为这不是一个好办法,有一个致命原因,不知道有没有人遇到过,我把栈内存改大后,与数据库建立不了连接了(ADO方式,Acess数据库),把栈内存还原,问题立刻消失。不知道究竟是什么原因,有知道的可以告诉我。email:la_第二种解决办法是比较可行的,具体实现由很多种方法可以直接把数组定义改成指针,然后动态申请内存;也可以把局部变量变成全局变量,一个偷懒的办法是直接在定义前边加个static,呵呵,直接变成静态变量(实质就是全局变量)。即可以把上例中的函数这么写:voidtest_stack_overflow(){staticcharchdata[2*];}当然,除非万不得已,尽量不要使用这么大的数组,出现这种情况多半说明程序结构有问题。
引用地址:堆栈溢出一般是什么原因导致的?_西安联嵌吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:3贴子:
堆栈溢出一般是什么原因导致的?
堆栈溢出一般是什么原因导致的?
票牛教你如何买到热门、便宜、真实的演出门票!
解答:堆栈溢出一般都是由堆栈越界访问导致的。例如函数内局部变量数组越界访问,或者函数内局部变量使用过多,超出了操作系统为该进程分配的栈的大小也会导致堆栈溢出。
深度解析:首先要区分清楚堆、栈、堆栈这几个名词。堆(heap)和栈(stack)是两种不同的内存管理机制。堆被称为动态内存,由堆管理器(系统里的大人物,山高皇帝远不用去管它)管理,程序中可以使用malloc函数来(向堆管理器)申请分配堆内存,使用完后使用free函数释放(给堆管理器回收)。堆内存的特点是:在程序运行过程中才申请分配,在程序运行中即释放(因此称为动态内存分配技术)。栈是C语言使用的一种内存自动分配技术(注意是自动,不是动态,这是两个概念),自动指的是栈内存操作不用C程序员干预,而是自动分配自动回收的。C语言中局部变量就分配在栈上,进入函数时局部变量需要的内存自动分配,函数结束退出时局部变量对应的内存自动释放,整个过程中程序员不需要人为干预。
堆栈这个词纯粹是用来坑人的。堆就是堆(heap),栈就是栈(stack),根本没有另外一种内存管理机制叫堆栈。大多数时候有人说起堆栈,其实他想说的是栈,以前早些的时候,这方面的命名并不是特别准确。(别人说堆栈的时候,大家知道他其实想说的是栈就行了,自己就不要再用这个不准确的词了)。既然堆和栈都是用来管理内存的机制,使用时就有一定的规则。无视规则的错误使用(C语言设计时赋予了程序员很大的自由度,所以有些错误语言本身是不会检查的,全凭程序员自己把握。)就可以导致一些内存错误,如内存泄漏、溢出错误等。内存泄漏主要发生在堆内存使用中。譬如我们使用malloc申请了内存,使用过后并未释放而丢弃了指向该内存的指针(这个指针是这段内存的唯一记录,程序中释放该段内存都靠这个指针了),那么这段堆内存就泄漏掉了(堆管理器以为程序还在使用,所以不会将这段内存再次分配给别的程序)。必须等到这个程序彻底退出后,系统回收该程序所使用的所有资源(申请的内存,使用的文件描述符等)时这些泄漏的内存才重新回到堆管理器的怀抱。
内存溢出在堆和栈中都有可能发生。参见章节示例1_2_stack_overflow.c中的8个示例函数,其中前三个函数与堆溢出有关,后五个函数与栈溢出有关。&堆溢出&函数heap_overflow中使用malloc申请了16字节动态内存,然后尝试去读写这16个内存之中的第n个。三个测试分别给n赋值9,99和9999999,得到的结果很有意思(见程序后面的注释,大家也可以自己编译运行测试),现在我们来探讨其中的原理。
n等于9的时候没什么好说的,本该正确运行,这个相信大家没有异议。n等于99的时候······竟然也可以正确运行,这个相信很多人就有点想不通了。我们申请的空间只有16字节啊,怎么竟然还可以访问第99个字节空间呢(这就是所谓的堆溢出访问)?这时候实际已经堆溢出了,但是为什么结果没有出错呢?原因在操作系统的内存分配策略中。譬如linux中内存是按照页(Page,一般是4K字节一个页)来管理的,操作系统给进程分配内存本质上都是以页为单位进行的。也就是说你虽然只要求了16个字节,但是实际分配给你这个进程的可能是一个页(4K字节)。这个页中只有这16个字节是你自己的“合法财产”,其他部分你不该去访问(一访问就堆越界)。但是因为操作系统对内存的访问权限管理是以页为单位的,因此本页内16字节之外的内存你(非法)访问时系统仍然不会报错,并且确实能够达成目的(示例中n等于99时读写仍然正确)。那是不是说堆越界是无害的,完全不用担心呢?显然不是。因为堆越界最大的伤害不是对自己,而是对“别人”。因为除了你申请的16字节外本页面内其他内存可能会被堆管理器分配给其他变量,你越界访问时意味着你可能践踏了其他变量的有效区域(譬如我们给第99个字节赋值为g时,很可能把别处动态分配的一个变量的一部分给无意识的修改了)。因此其他变量会“莫名其妙”的出错,而且最可怕的是这种出错编译器无法帮你发现,大多数时候隐藏的很深,极难发现,往往令调试者抓狂、痛不欲生。因此访问堆内存时应该极为小心,一定要检验访问范围,谨防堆访问越界。
最后一个示例中n等于9999999,这是我随便写的一个很大的数,执行结果为:段错误(Segmentation fault)。熟悉C语言的同学都知道,一般段错误都是因为程序访问了不该访问的区域(譬如试图写代码段),这里也不例外。什么原因?考虑下上文中提到的以页为单位的内存管理策略。给你分配了一个页(一般是4KB),你访问时索引值太大已经超出了这个页(跑到下个页甚至更后面的页面去了),那边的内存页面根本不归你使用,你试图读写的时候操作系统的内存管理部分就会一巴掌把你扇回来,给你个Segmentation fault。那个数字式我随便写的,你也可以自己试试先给个小数字,然后逐渐加大,总会有个临界点,过了那个点就开始段错误了。&栈溢出&func1到func5这五个示例用来演示栈溢出。func1是典型的数组越界造成的栈溢出,压栈越界导致冲毁了函数调用堆栈结构,致使整个程序崩溃。由此可见,在C语言中数组访问时一定要小心检查,保证不越界。C语言为了追求最高的效率,并未提供任何数组访问动态检查(实际上也没有提供编译时数组访问是否越界的静态检查,其原因是C语言愿意相信程序员,而将检查的重任交给了程序员自己······果然是权力越大、责任越大啊!),因此“保卫世界和平的重任就靠你了”。
func2和func3是一对对比测试。其中调用了一个递归函数factorial,该函数用来求一个正整数n的阶乘。func2中n等于10,计算结果为3628800,是正确的(大家可以用计算器自己验证)。func3中n等于,运行结果为段错误(其实即使不段错误,factorial函数本身也无法计算很大数字的阶乘,原因在于函数中使用unsigned int类型来存阶乘值,这个类型的取值范围非常有限,n稍微大一点就会溢出。但溢出只会导致计算结果不对,不会造成段错误的)。怎么会段错误呢?因为递归次数太多,栈终于被撑爆了。递归函数运行时,实际上相当于不停在执行子函数调用,因此栈一直在分配而没有释放。若在栈使用完之前递归仍然没有结束返回(此时会逐层释放栈)就会发生段错误。这是栈溢出的另一个典型情况,请大家以后使用递归算法解决问题时注意这个限制。func4和func5是一对对比测试。其中均定义了一个局部变量数组a,不同的是a的大小。func4中数组大小为1M(注意a的类型是int,因此这里单位是4字节),运行成功。而func5中数组大小为4M,运行时则发生段错误。相信有了上面上面的讲解,大家能够很容易想明白,局部变量分配太多把栈用完了,所以就段错误了,就这么简单。
以上,通过5个示例程序为大家演示了栈溢出的三种情况。一般来说,第一种情况是明显的错误,且每次执行都确定会发生错误。而后两种错误则稍微复杂一些,原因在于这两种错误都依赖于栈的大小。而栈的大小在操作系统中不是固定的,是可以人为设置的(譬如linux中使用ulimit –s来查看和设置用户进程栈大小)。这就会带来一些很“神奇”的bug,如程序在你的计算机中运行良好,调试通过。结果发给客户,10个客户中8个运行良好,另外两个会报错、死机······这时候只要重新设置一个更大的用户栈容量就可以解决问题。所以大家在写代码时一定要注意,考虑到你的代码有可能潜在的问题。这样一旦问题暴露即可迅速定位,并最快的找到解决方案。不过更高级的做法是:在写代码时尽量减少可能存在的问题,让你的程序尽量更加健壮(robust)。
代码如下:#include
#include #include // function prototype declarationint heap_overflow(unsigned int n, char c);void func1(void);void func2(void);void func3(void);void func4(void);void func5(void);// 注意:每个函数需要单独执行测试,因此在测试每个函数时,需要将其他函数屏蔽。int main(void)
// 堆溢出访问演示//heap_overflow(9, *t*); // The 9th element = t.//heap_overflow(99, *g*); // The 99th element = g.heap_overflow(9999999, *g*); // Segmentation fault// 栈溢出访问演示 //func1(); // stack smashing detected//func2(); // factorial(10) = 3628800.//func3(); // Segmentation fault//func4(); // a[] = 5.//func5(); // Segmentation fault
} int heap_overflow(unsigned int n, char c){char *p = NULL;p = (char *)malloc(16);if (NULL == p){printf(&fail to get dynamic memory from heap.\n&);return -1;}memset(p, 0, 16);*(p + n) =printf(&The %dth element = %c.\n&, n, *(p + n));free(p);p = NULL;return 0;}void func1(void){char name[8];
strcpy(name, &linus tovards.&);printf(&Hello, %s!&, name);
}static unsigned int factorial(unsigned int n){if (n == 1)return 1;elsereturn n * factorial(n - 1);}void func2(void){printf(&factorial(10) = %d.\n&, factorial(10));}void func3(void){printf(&factorial() = %d.\n&, factorial());}#define M (1 * 1024 * 1024)#define N (4 * 1024 * 1024)void func4(void){int a[M];a[M-1] = 5;printf(&a[%d-1] = %d.\n&, M, a[M-1]);}void func5(void){int a[N];a[N-1] = 5;printf(&a[%d-1] = %d.\n&, N, a[N-1]);}
贴吧热议榜
使用签名档&&
保存至快速回贴百度拇指医生
&&&普通咨询
您的网络环境存在异常,
请输入验证码
验证码输入错误,请重新输入后使用快捷导航没有帐号?
查看: 8630|回复: 8
怎样才能有效地控制堆栈溢出
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
& & & & & & & & & & & & & & & &&&我用得是keil&&MDK3.4 芯片是STM32F103ZET6,可是最近老出些莫名奇妙的错误,有人说是堆栈溢出,在程序中怎样编写才能有效地避免堆栈溢出
&&&&&&&&&&
在线时间1 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一、局部变量不要使用太多,尤其是局部大数组,最是杀堆栈的;
二、函数调用纵深不要太大;
三、尽量给堆栈留有余地;
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
& & & & & & & & & & & & & & & &&&谢谢楼上,以后在这方面我会注意的,谁还有别的意见
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
cortex M3 有个内存保护和异常功能.
利用它可以捕捉堆栈溢出的情况, 如果要求很高, 应该可以写出故障恢复的程序来.
在线时间15 小时
TA的帖子TA的资源
一粒金砂(中级), 积分 84, 距离下一级还需 116 积分
一粒金砂(中级), 积分 84, 距离下一级还需 116 积分
不知道在MDK&&(keil4)下能不能像IAR一样设置堆栈的大小!
在线时间0 小时
TA的帖子TA的资源
一粒金砂(中级), 积分 10, 距离下一级还需 190 积分
一粒金砂(中级), 积分 10, 距离下一级还需 190 积分
keil mdk 中在启动文件中有,可以修改
下面是启动文件中的代码:
Stack_Size& && &EQU& &&&0x
& && && && && & AREA& & STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem& && & SPACE& &Stack_Size
__initial_sp
应该可以修改。
在线时间2086 小时
威望50401分
芯币38684枚
TA的帖子TA的资源
楼上各位说的都很好,来学习一下
在线时间406 小时
芯币8628枚
TA的帖子TA的资源
纯净的硅(初级), 积分 663, 距离下一级还需 137 积分
纯净的硅(初级), 积分 663, 距离下一级还需 137 积分
支持。我也遇到过这种问题
在线时间4 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
资源大师勋章
在下载中心贡献超过4000份资料
Powered by
逛了这许久,何不进去瞧瞧?

我要回帖

更多关于 stm32 修改堆栈大小 的文章

 

随机推荐