什么导致堆栈溢出?

6
您可能认为这只是巧合,我的问题主题与论坛名称相似,但实际上我是通过谷歌搜索术语“堆栈溢出”而来的。我使用OPNET网络模拟器,其中我使用C编程。我认为我遇到了大数组大小的问题。似乎我碰到了某种内存分配限制。这可能与OPNET、Windows、我的笔记本电脑内存或最有可能的C语言有关。当我尝试使用嵌套数组时,问题就会出现,总元素数达到几千个整数。我认为我超过了总体内存分配限制,想知道是否有办法增加这个限制。
以下是确切的问题描述: 基本上我有一个路由表。我们称之为routing_tbl[n],意思是我支持30个节点(路由器)。现在,对于此表中的每个节点,我都保留有关许多(数百)可用路径的信息,在名为paths[p]的数组中。同样,对于此数组中的每个路径,我都在名为hops[h]的数组中保留了属于它的节点列表。因此,我至少使用nph个整数的内存,但此表还包含其他信息。在同一函数中,我还使用另一个嵌套数组,它也消耗了将近40,000个整数。一旦我运行模拟,它就会退出并抱怨堆栈溢出。当我减少路由表的总大小时,它可以正常工作。您认为问题是什么原因引起的,如何解决?非常感谢。

你能更明确地解释问题,而不是你目前的解决方案吗?我认为这会鼓励更多的人阅读和回答你的问题。 - Mats Fredriksson
6个回答

10

如果您能够贴出一些代码,那么这可能会有所帮助。请编辑问题并包含有问题的函数及其错误信息。

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

栈溢出的两个主要原因是:1)递归函数;2)分配了大量本地变量。

递归

如果您的函数像这样调用自身:

int recurse(int number) {

    return (recurse(number));
}

由于局部变量和函数参数存储在栈上,如果填充栈会导致堆栈溢出。

大的局部变量

如果你尝试分配一个大的局部变量数组,那么可以很轻松地一次性溢出栈。像这样的函数可能会引起问题:

void hugeStack (void) {

    unsigned long long reallyBig[100000000][1000000000];

    ...
}

关于这个类似的问题,有一个相当详细的回答,请参考这里


在技术上,递归函数将被尾递归优化为一个简单的无限循环,但让我们不要对这个例子太过苛求。 - Andrew Johnson
根据描述,问题似乎是由于堆栈上推送了过多的数据,而不是递归引起的。我打赌将变量放在堆上(使用malloc)可以解决这个问题。 - Jon Ericson
是的,这就是我最初得到的答案,但后来我想他可能正在使用某些函数遍历他的网络。我想我只需要添加一个基本的参考答案,任何其他人走过来的人都可以得到一些线索。谁知道呢,这个新手可能永远不会回来 :) - Andrew Johnson

3

您的堆栈使用量很高。可能的原因包括:在堆栈上创建路由表、在堆栈上传递路由表,或者通过递归处理整个内容生成了大量调用。

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


在C语言中,如何将数组作为堆栈参数传递? - Alexander

1

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

这种错误也可能是由于在静态声明中分配了太多内存所致。您可以通过使用malloc()进行动态分配来解决此类问题。

您不能在此程序上使用调试器吗?有什么原因呢?


我知道我很挑剔,但是深度不是“高”,而是“深”。抱歉。 - Cyberherbalist

1

这取决于您在哪里声明变量。

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

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


1
许多操作系统在您使用更多堆栈时动态扩展堆栈。当您开始写入刚超出堆栈的内存地址时,操作系统会假定您的堆栈已经增长了一点,并为其分配额外的一页(在x86上通常为4096Kib,正好为1024个int)。
问题是,在x86(和其他一些体系结构)上,堆栈向下增长,但C数组向上增长。这意味着如果您访问大型数组的开头,您将访问距离堆栈边缘超过一页的内存。
如果您从数组的末尾开始将数组初始化为0(没错,用for循环来完成),则错误可能会消失。如果确实如此,则这就是问题所在。
您可能能够找到一些操作系统API函数来强制进行堆栈分配,或者编译器pragma /标志。我不确定如何以可移植的方式完成此操作,除了使用malloc()和free()!

0

用非线程化编译的 C 代码,除非你做了什么特别过分的事情,例如无限递归或宇宙级内存泄漏,否则很难遇到堆栈溢出问题。但是,您的模拟器可能有一个线程包,会强制实施堆栈大小限制。当你启动一个新线程时,它将为该线程分配一块用于堆栈的内存块。很可能有一个参数可以设置默认的堆栈大小,或者有一种方法可以动态增加堆栈。例如,pthreads 有一个函数 pthread_attr_setstacksize(),在启动新线程之前调用该函数来设置其大小。您的模拟器可能正在使用 pthreads,也可能不是。请参阅模拟器参考文档。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接