提问人: 提问时间:9/20/2008 最后编辑:Paŭlo Ebermann 更新时间:8/24/2011 访问量:6488
是什么导致了堆栈溢出?
What is causing a stack overflow?
问:
你可能会认为这是一个巧合,我的问题主题与论坛的名称相似,但我实际上是通过谷歌搜索“堆栈溢出”一词来到达这里的。
我使用OPNET网络模拟器,其中我使用C语言进行编程。我想我在大数组大小方面遇到了问题。似乎我遇到了某种内存分配限制。它可能与 OPNET、Windows、我的笔记本电脑内存或最有可能的 C 语言有关。当我尝试使用嵌套数组时,会导致该问题,其元素总数为数千个整数。我认为我超出了总体内存分配限制,我想知道是否有办法增加此上限。 以下是确切的问题描述:
我基本上有一个路由表。我们称之为 routing_tbl[n],这意味着我支持 30 个节点(路由器)。现在,对于此表中的每个节点,我都会保留信息。大约许多(数百个)可用路径,在一个名为 paths[p] 的数组中。同样,对于此数组中的每条路径,我将属于它的节点列表保留在名为 hops[h] 的数组中。因此,我使用了至少 nph 整数的内存,但此表还包含其他信息。在同一个函数中,我还使用了另一个嵌套数组,该数组也消耗了近 40,000 个整数。 一旦我运行我的模拟,它就不再抱怨堆栈溢出了。当我减小路由表的总大小时,它会起作用。 您认为是什么原因导致了问题,如何解决? 非常感谢 阿里
答:
如果您发布一些代码,可能会有所帮助。编辑问题以包括问题函数和错误。
同时,这是一个非常通用的答案:
堆栈溢出的两个主要原因是 1) 递归函数,或 2) 大量局部变量的分配。
递归
如果你的函数调用自己,如下所示:
int recurse(int number) {
return (recurse(number));
}
由于局部变量和函数参数存储在堆栈上,因此它将填充堆栈并导致堆栈溢出。
大型局部变量
如果您尝试分配大量局部变量,则可以轻松地溢出堆栈。像这样的函数可能会导致问题:
void hugeStack (void) {
unsigned long long reallyBig[100000000][1000000000];
...
}
这个类似的问题有一个相当详细的答案。
评论
当嵌入式递归调用的数量过高时,C 语言中可能会发生堆栈溢出。也许您从自身调用函数的次数太多了?
此错误也可能是由于在静态声明中分配了过多的内存。您可以通过 malloc() 切换到动态分配来解决此类问题。
是否有原因不能在此程序上使用调试器?
评论
不知何故,您使用了很多堆栈。可能的原因包括您在堆栈上创建路由表,在堆栈上传递路由表,或者您正在生成大量调用(例如,通过递归处理整个过程)。
在前两种情况下,您应该在堆上创建它,并传递指向它的指针。在第三种情况下,您需要以迭代形式重写算法。
评论
这取决于您声明变量的位置。
局部变量(即在堆栈上声明的变量受最大帧大小的限制)这是您正在使用的编译器的限制(通常可以使用编译器标志进行调整)。
动态分配的对象(即堆上的对象)受可用内存量的限制。这是操作系统的一个属性(从技术上讲,如果您有智能操作系统,则可以通过更大的物理内存来增加物理内存)。
你不太可能遇到无线程编译的 C 的堆栈溢出,除非你做了一些特别令人震惊的事情,比如失控的递归或宇宙内存泄漏。但是,模拟器可能有一个线程包,该包将施加堆栈大小限制。当您启动一个新线程时,它将为该线程的堆栈分配一块内存。很可能,您可以在某处设置一个参数来建立默认堆栈大小,或者可能有一种方法可以动态增长堆栈。例如,pthreads 有一个函数 pthread_attr_setstacksize(),您可以在启动新线程之前调用该函数来设置其大小。您的模拟器可能使用也可能不使用 pthreads。请查阅模拟器参考文档。
许多操作系统会随着您使用更多堆栈而动态扩展堆栈。当您开始写入堆栈之外的内存地址时,操作系统会假设您的堆栈刚刚增长了一点,并为其分配了一个额外的页面(通常为 x86 上的 4096Kib - 正好是 1024 整数)。
问题是,在 x86(和其他一些架构)上,堆栈向下增长,但 C 数组向上增长。这意味着,如果您访问大型数组的开头,您将访问距离堆栈边缘超过一页的内存。
如果从数组末尾开始将数组初始化为 0(没错,请创建一个 for 循环来执行此操作),错误可能会消失。如果他们这样做了,这确实是问题所在。
您也许可以找到一些操作系统 API 函数来强制堆栈分配或编译器编译指示/标志。我不确定如何可移植地完成此操作,当然除了使用 malloc() 和 free()!
评论