【问题标题】:Getting a stack overflow exception when declaring a large array声明大数组时出现堆栈溢出异常
【发布时间】:2024-05-04 06:20:03
【问题描述】:

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

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 捆绑在一起。

标签: c arrays memory stack allocation


【解决方案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种方式:

  1. 在堆上分配数组 - 使用 malloc(),正如其他海报所建议的那样。不要忘记free()它(尽管对于main()它并不重要 - 操作系统会在程序终止时为您清理内存)。
  2. 在单元级别声明数组 - 它将分配在数据段中并且对所有人可见(在声明中添加 static 将限制单元的可见性)。
  3. 将您的数组声明为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 可以很好地满足我的目的