是什么导致堆栈溢出?

时间:2020-03-06 14:28:10  来源:igfitidea点击:

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

我使用OPNET网络模拟器,在其中使用C进行编程。我认为阵列尺寸过大存在问题。看来我遇到了某种内存分配限制。它可能与OPNET,Windows,我的笔记本电脑内存或者最可能的C语言有关。当我尝试使用嵌套数组且元素总数达到几千个整数时,就会引起问题。我认为我已经超出了整体内存分配限制,并且我想知道是否有办法提高此上限。
这是确切的问题描述:

我基本上有一个路由表。我们将其称为routing_tbl [n],这意味着我支持30个节点(路由器)。现在,对于此表中的每个节点,我保留信息。在称为path [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(),我们可以在启动新线程以设置其大小之前调用该函数。模拟器可能使用或者可能不使用pthread。请查阅模拟器参考文档。

当我们使用更多堆栈时,许多操作系统都会动态扩展堆栈。当我们开始向堆栈之外的内存地址写入数据时,操作系统会假设堆栈增加了一点,并为其分配了额外的页面(通常在x86上为4096Kib,恰好为1024 ints)。

问题是,在x86(和其他一些体系结构)上,堆栈向下增长,但C阵列向上增长。这意味着,如果我们访问大型数组的开始,那么我们将访问的内存距离堆栈边缘多于一页。

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

我们也许可以找到一些OS API函数来强制进行堆栈分配或者编译器编译指示/标志。除了使用malloc()和free()之外,我不确定该如何进行移植!