是什么导致了堆栈溢出?

What is causing a stack overflow?

提问人: 提问时间:9/20/2008 最后编辑:Paŭlo Ebermann 更新时间:8/24/2011 访问量:6488

问:

你可能会认为这是一个巧合,我的问题主题与论坛的名称相似,但我实际上是通过谷歌搜索“堆栈溢出”一词来到达这里的。

我使用OPNET网络模拟器,其中我使用C语言进行编程。我想我在大数组大小方面遇到了问题。似乎我遇到了某种内存分配限制。它可能与 OPNET、Windows、我的笔记本电脑内存或最有可能的 C 语言有关。当我尝试使用嵌套数组时,会导致该问题,其元素总数为数千个整数。我认为我超出了总体内存分配限制,我想知道是否有办法增加此上限。 以下是确切的问题描述:

我基本上有一个路由表。我们称之为 routing_tbl[n],这意味着我支持 30 个节点(路由器)。现在,对于此表中的每个节点,我都会保留信息。大约许多(数百个)可用路径,在一个名为 paths[p] 的数组中。同样,对于此数组中的每条路径,我将属于它的节点列表保留在名为 hops[h] 的数组中。因此,我使用了至少 nph 整数的内存,但此表还包含其他信息。在同一个函数中,我还使用了另一个嵌套数组,该数组也消耗了近 40,000 个整数。 一旦我运行我的模拟,它就不再抱怨堆栈溢出了。当我减小路由表的总大小时,它会起作用。 您认为是什么原因导致了问题,如何解决? 非常感谢 阿里

C 内存 malloc 堆栈溢出

评论

0赞 Mats Fredriksson 9/20/2008
您能更明确地解释这个问题,而不是您当前的解决方案吗?我认为这会鼓励更多的人阅读和回答您的问题。

答:

10赞 Andrew Johnson 9/20/2008 #1

如果您发布一些代码,可能会有所帮助。编辑问题以包括问题函数和错误。

同时,这是一个非常通用的答案:

堆栈溢出的两个主要原因是 1) 递归函数,或 2) 大量局部变量的分配。

递归

如果你的函数调用自己,如下所示:

int recurse(int number) {

    return (recurse(number));
}

由于局部变量和函数参数存储在堆栈上,因此它将填充堆栈并导致堆栈溢出。

大型局部变量

如果您尝试分配大量局部变量,则可以轻松地溢出堆栈。像这样的函数可能会导致问题:

void hugeStack (void) {

    unsigned long long reallyBig[100000000][1000000000];

    ...
}

这个类似的问题有一个相当详细的答案。

评论

0赞 Andrew Johnson 9/20/2008
从技术上讲,递归函数将被尾部优化为一个简单的无限循环,但我们不要对示例过于偏执:)
0赞 Jon Ericson 9/20/2008
鉴于描述,这听起来像是在堆栈上推送了太多数据而不是递归的问题。我敢打赌将变量放在堆上(使用 malloc)将解决问题。
0赞 Andrew Johnson 9/20/2008
是的,这就是我一开始得到的答案,但后来我认为他可能正在通过某种功能遍历他的网络。我想我只需要添加一个基本的参考答案,任何以这种方式徘徊的人都可以得到线索。谁知道呢,这个菜鸟可能再也回不来了:)
1赞 tkerwin 9/20/2008 #2

当嵌入式递归调用的数量过高时,C 语言中可能会发生堆栈溢出。也许您从自身调用函数的次数太多了?

此错误也可能是由于在静态声明中分配了过多的内存。您可以通过 malloc() 切换到动态分配来解决此类问题。

是否有原因不能在此程序上使用调试器?

评论

0赞 Cyberherbalist 9/20/2008
挑剔,我知道,但深度不是“高”,而是“深”。不好意思。
3赞 user11318 9/20/2008 #3

不知何故,您使用了很多堆栈。可能的原因包括您在堆栈上创建路由表,在堆栈上传递路由表,或者您正在生成大量调用(例如,通过递归处理整个过程)。

在前两种情况下,您应该在堆上创建它,并传递指向它的指针。在第三种情况下,您需要以迭代形式重写算法。

评论

0赞 Alexander 9/20/2008
如何在 C 中的堆栈上传递数组?
1赞 Martin York 9/20/2008 #4

这取决于您声明变量的位置。

局部变量(即在堆栈上声明的变量受最大帧大小的限制)这是您正在使用的编译器的限制(通常可以使用编译器标志进行调整)。

动态分配的对象(即堆上的对象)受可用内存量的限制。这是操作系统的一个属性(从技术上讲,如果您有智能操作系统,则可以通过更大的物理内存来增加物理内存)。

0赞 mxg 9/20/2008 #5

你不太可能遇到无线程编译的 C 的堆栈溢出,除非你做了一些特别令人震惊的事情,比如失控的递归或宇宙内存泄漏。但是,模拟器可能有一个线程包,该包将施加堆栈大小限制。当您启动一个新线程时,它将为该线程的堆栈分配一块内存。很可能,您可以在某处设置一个参数来建立默认堆栈大小,或者可能有一种方法可以动态增长堆栈。例如,pthreads 有一个函数 pthread_attr_setstacksize(),您可以在启动新线程之前调用该函数来设置其大小。您的模拟器可能使用也可能不使用 pthreads。请查阅模拟器参考文档。

1赞 Artelius 10/30/2008 #6

许多操作系统会随着您使用更多堆栈而动态扩展堆栈。当您开始写入堆栈之外的内存地址时,操作系统会假设您的堆栈刚刚增长了一点,并为其分配了一个额外的页面(通常为 x86 上的 4096Kib - 正好是 1024 整数)。

问题是,在 x86(和其他一些架构)上,堆栈向下增长,但 C 数组向增长。这意味着,如果您访问大型数组的开头,您将访问距离堆栈边缘超过一页的内存。

如果从数组末尾开始将数组初始化为 0(没错,请创建一个 for 循环来执行此操作),错误可能会消失。如果他们这样做了,这确实是问题所在。

您也许可以找到一些操作系统 API 函数来强制堆栈分配或编译器编译指示/标志。我不确定如何可移植地完成此操作,当然除了使用 malloc() 和 free()!