声明大数组时出现堆栈溢出异常

Posted

技术标签:

【中文标题】声明大数组时出现堆栈溢出异常【英文标题】:Getting a stack overflow exception when declaring a large array 【发布时间】:2022-01-02 16:30:54 【问题描述】:

以下代码为我生成堆栈溢出错误

int main(int argc, char* argv[])

    int sieve[2000000];
    return 0;

我该如何解决这个问题?我正在使用 Turbo C++,但想将我的代码保留在 C 中

编辑:

感谢您的建议。上面的代码只是举例,我实际上在函数中声明了数组,而不是在 sub main 中。另外,我需要将数组初始化为零,所以当我搜索 malloc 时,我发现 calloc 非常适合我的目的。

Malloc/calloc 比在堆栈上分配还具有允许我使用变量声明大小的优势。

【问题讨论】:

我读到“堆栈溢出异常”,心里想“......?网站有问题吗?”显然我在这里花了太多时间:-/ 我很确定这种类型的问题以前一定在这个网站上出现过,但是搜索“堆栈溢出”是没有用的 我认为每个 C 程序员最终都会浪费大量时间第一次解决这个问题.. Turbo C++ 是 16 位应用程序,这意味着它使用内存分段,每个分段大小为 64KB,因此没有结构可以大于此数字,总内存使用量最大为 640KB ( 1MB 或更多,带有一些扩展内存管理器)。为什么要使用这样一个有 20 多年历史的编译器? 现在希望你已经发现了 GCC。在其他地方它与 Code::Blocks IDE 捆绑在一起。 【参考方案1】:

您的数组太大而无法放入堆栈,请考虑使用堆:

int *sieve = malloc(2000000 * sizeof(*sieve));

如果你真的想改变堆栈大小,take a look at this document.

提示: - 不要忘记在不再需要动态分配的内存时释放它。

【讨论】:

因为这是 C,你不需要(实际上也不应该)强制转换 malloc 的返回值。 你为什么不转换 malloc 的结果呢?您是否不必将其从 void* 中强制转换才能使用它做很多事情? @yodaj007:你不需要明确地转换它。由于赋值变量也是指针类型,赋值执行隐式转换。 @Amy 阅读:"Do I cast the result of malloc?"。它解释了为什么 在 C 中强制转换 malloc(或任何其他内存分配函数)的几个原因。 相关:C++ 规范问答,建议针对同一问题使用 new/delete 或 std::vector:Segmentation fault on large array sizes【参考方案2】:

有3种方式:

    在堆上分配数组 - 使用 malloc(),正如其他海报所建议的那样。不要忘记free()它(尽管对于main()它并不重要 - 操作系统会在程序终止时为您清理内存)。 在单元级别声明数组 - 它将分配在数据段中并且对所有人可见(在声明中添加 static 将限制单元的可见性)。 将您的数组声明为static - 在这种情况下,它将在数据段中分配,但仅在main() 中可见。

【讨论】:

我只是让它静态:main() 应该只被调用一次,所以没有陷阱;这里不需要malloc()...【参考方案3】:

最好将它分配在堆上,而不是堆栈上。像

int main(int argc, char* argv[])

    int * sieve;
    sieve = malloc(20000);
    return 0;

【讨论】:

并检查筛子是否为 NULL “我只为你筛子”--卓涵 我认为您的意思是 sieve = malloc(20000 * sizeof *sieve) - 除非您的平台具有大小为 1 的 int(即使那样,我也不会在代码中嵌入该假设)。【参考方案4】:

这大约是 7MB 的堆栈空间。在 Visual Studio 中,您将使用 /STACK:###,### 来反映您想要的大小。如果你真的想要一个巨大的堆栈(可能是一个很好的理由,使用 LISP 或其他东西:),即使堆在强制你使用 VirtualAlloc 之前也仅限于 small'sh 分配),你可能还想将你的 PE 设置为构建/LARGEADDRESSAWARE(又是 Visual Studio 的链接器),但此配置是您的 PE 标头,以允许您编译的二进制文件寻址完整的 4GB 32 位地址空间(如果在 WOW64 中运行)。如果构建真正庞大的二进制文件,您通常还需要将 /bigobj 配置为额外的链接器参数。

如果你仍然需要更多空间,你可以通过使用类似于(再次 MSVC 的链接)/merge: 的东西来彻底违反约定,这将允许你将一个部分打包到另一个部分中,这样你就可以使用每个单个字节单个共享代码/数据部分。当然,您还需要在 def 文件或 #pgrama 中配置 SECTIONS 权限。

【讨论】:

【参考方案5】:

使用 malloc。全部检查返回类型是否为 null,如果为 null,则您的系统根本没有足够的内存来容纳那么多值。

【讨论】:

【参考方案6】:

你的数组很大。

您的机器或操作系统可能没有或不想分配这么多内存。


如果你绝对需要一个巨大的数组,你可以尝试动态分配它(使用malloc(...)),但是你有内存泄漏的风险。不要忘记释放内存。

malloc 的优点是它尝试在堆而不是堆栈上分配内存(因此不会出现堆栈溢出)。

您可以检查 malloc 返回的值以查看分配是成功还是失败。 如果失败,只需尝试 malloc 一个较小的数组。


另一种选择是使用可以动态调整大小的不同数据结构(如链表)。此选项是否合适取决于您将如何处理数据。

另一种选择是将内容存储在文件中,即时流式传输数据。这种方法最慢。

如果你想在硬盘上存储,你还不如使用现有的库(用于数据库)

【讨论】:

【参考方案7】:

由于 Turbo C/C++ 是 16 位编译器 int 数据类型,因此消耗大约 2 个字节。 2bytes*2000000=40,00,000 bytes=3.8147MB 空间。

函数的自动变量存储在堆栈中,导致堆栈内存溢出。而是使用数据内存 [使用静态或全局变量] 或动态堆内存 [使用 malloc/calloc] 根据处理器内存映射的可用性创建所需的内存。

【讨论】:

【参考方案8】:

是否有某些原因不能使用 alloca() 根据对象实际需要的大小在堆栈帧上分配所需的空间?

如果你这样做了,但仍然破坏了堆栈,请将其放入已分配的堆中。我强烈建议不要在 main() 中将其声明为静态并将其放入数据段中。

如果它真的必须那么大并且您的程序不能在堆上分配它,那么您的程序一开始就真的没有业务在这种类型的机器上运行。

您(确切地)想要完成什么?

【讨论】:

我正在使用 ProjectEuler.net 中的问题来学习 C,并且正在实施埃拉托色尼筛算法,所以它必须那么大。 malloc 可以很好地满足我的目的

以上是关于声明大数组时出现堆栈溢出异常的主要内容,如果未能解决你的问题,请参考以下文章

Expecto FsCheck在生成字符串时出现堆栈溢出异常

使用 dynamic_pointer_cast 时出现异常 0xC0000409(堆栈缓冲区溢出)[关闭]

运行递归线性搜索时出现堆栈溢出错误

Visual Studio C/C++ 数组大小未处理的异常堆栈溢出 [重复]

大数组的堆栈溢出但没有同样大的向量?

基于堆栈的缓冲区溢出