声明大数组时出现堆栈溢出异常
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 ofmalloc
?"。它解释了为什么 不 在 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(堆栈缓冲区溢出)[关闭]