C-即使分配了较大的堆栈尺寸,如何消除堆栈溢出误差



Unhandled exception at 0x00AA9379 in A.exe: 0xC00000FD: Stack overflow (parameters: 0x00000000, 0x00802000).

我面临堆栈溢出错误。我正在使用VS15作为我的IDE。我试图为堆栈分配更多内存。为此,我将Project >> Properties >> Linker >> System >> Stack allocation并分配了4GB作为堆栈。但是错误继续停止在此行的chkstk.asm

99 sub eax, _PAGESIZE_ ; decrease by PAGESIZE

但是问题没有解决。我怎么会事先知道我需要多少堆栈尺寸?我使用了所有大变量的动态内存分配。但无法解决问题。这是一个可验证的示例...

这是我的代码:

#include <stdio.h>
void main(void)
{
    FILE    *fp1;
    char    datfile[132];
    int    nod[1024 * 1024];
    int    Enod[8 * 1024 * 1024];
    double    nodS[1024 * 1024], nodF[1024 * 1024];
}

使用MS编译器Windows上的默认堆栈大小为1 MIB。您将几个阵列放在堆栈中,可容纳数百万个整数和双打。

您说您将堆栈尺寸提高到4GB。在这种情况下,以下情况变得相关:

保留的内存大小表示虚拟内存中的总堆栈分配。因此,预留尺寸仅限于虚拟地址范围。最初犯的页面在引用之前不使用物理内存;但是,他们确实从系统总数限制中删除了页面,即页面文件的大小加上物理内存的大小。该系统根据需要从保留的堆栈内存中提交其他页面,直到堆栈到达保留尺寸减去一个页面(用作防止堆栈溢出的警卫页面(,否则系统在内存上的系统很低,以至于操作失败。(强调我的(

此外,英特尔的笔记列表

给定这些定义,以下列出了Windows的32位和64位变体的限制:

32位

  • 堆栈数据-1GB(堆栈大小由链接器设置,默认值为1MB。可以使用Linker Property System>堆栈储备大小来增加此功能(

...

64位

  • 堆栈数据-1GB(堆栈大小由链接器设置,默认值为1MB。可以使用Linker Property System>堆栈储备大小来增加此功能(

...

请注意,静态和堆栈数据的限制在32位和64位变体中都是相同的。 这是由于Windows Portable的格式所致可执行(PE(文件类型,用于描述链接器列出的EXES和DLL。它具有32位字段用于图像部分的偏移和长度,并且没有用于64位Windows的变体。与32位窗口一样,静态数据和堆栈共享相同的第一个2GB地址空间。(强调我的(

最后,仔细查看帖子开头的指令:

但是,错误继续停止在此行的chkstk.asm

99       sub     eax, _PAGESIZE_         ; decrease by PAGESIZE

要更加仔细地看一下,我编写了一个小程序,该程序会产生与您的效果相同的效果(仅使用32位构建"工作"&Mdash;我不确定需要做什么才能导致64--撞车可执行(:

#include <stdio.h>
#include <stdlib.h>
#define MYSIZE (8 * 1024 * 1024)
int main(void) {
    int x[MYSIZE];
    x[MYSIZE - 1] = rand();
    printf("%dn", x[MYSIZE - 1]);
    return 0;
}

我将链接选项中的堆栈大小设置为4294967296,然后在调试器下运行程序。它因堆栈溢出而崩溃,并以与您观察到的同一指令破裂。在堆栈检查代码中滚动,我注意到以下评论:

; Handle allocation size that results in wraparound.
; Wraparound will result in StackOverflow exception.

据我所知,例程试图一次将PAGESIZE的顶部堆栈移动以保留适用的堆栈大小。

因此,试图将堆栈大小设置为4 GB似乎是您直接问题的根本原因。您可以尝试将其设置为1 GB,从而解决该问题。确实,我将上述程序的堆栈大小更改为1073741823(= 1024 * 1024 * 1024 - 1(,但没有堆栈溢出。我认为link至少没有警告无效的堆栈尺寸值是一个错误。

的确,查看使用/STACK:1000000000构建的可执行文件的六角形,并将其与使用/STACK:4294967296构建的可执行

00000150: 0000 0000 0300 4081 00ca 9a3b 0010 0000  ......@....;....
00000150: 0000 0000 0300 4081 0000 0000 0010 0000  ......@.........

请注意,0x3b9aca00的十六进制为1,000,000,000。查看标题格式,指的是long SizeOfStackReserve;条目。也就是说,当您将堆栈大小设置为4 GB时(实际上是0xfffffffc)上方的任何内容,都将其设置为零。

将堆栈大小设置为较大但仍然不受支持的尺寸确实会导致在可执行标头中设置正值,例如:

 cl main.c/link/stack:0xDeadBeadxxd main.exe |更多...00000150:0000 0000 0300 4081 b0be adde 0010 0000 ......@............ 

无法运行最终的可执行文件:

 c: ...> main没有足够的存储空间来处理此命令。

但是,即使将堆栈设置为较小但仍然大的尺寸可能使您的程序可以运行,但依靠巨大的堆栈并不一定是一个好主意。直接的选择是使用malloc在堆上分配这些数组并记住它们。

也就是说,而不是

int    Enod[8 * 1024 * 1024];

您需要声明int *Enod,然后使用

为数组分配内存
Enod = malloc(8 * sizeof(*Enod) * 1024 * 1024);
/* remember to check that Enod is not NULL */

回答此问题讨论为什么堆栈更受限制。

此外,您的代码将受益于使用定义的有意义的mnemonics替换任意外观的数字。

用malloc'd Array使用的代替替换堆栈数组。繁荣!解决问题。

当然,您可能仍然会算完内存,但是至少您不必努力避免这种情况,而这将是堆内的内存,而IMO更容易获得更多。

您也可以预先分配其中一些malloc'd数组,以便代码不会做每个功能的工作太多。

最新更新