声明大数组时出现堆栈溢出异常
Posted
技术标签:
【中文标题】声明大数组时出现堆栈溢出异常【英文标题】:Getting a stack overflow exception when declaring a large array 【发布时间】:2021-10-23 06:36:53 【问题描述】:以下代码为我生成堆栈溢出错误
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】:是否有某些原因不能使用 alloca() 根据对象实际需要的大小在堆栈帧上分配所需的空间?
如果你这样做了,但仍然破坏了堆栈,请将其放入已分配的堆中。我强烈建议不要在 main() 中将其声明为静态并将其放入数据段中。
如果它真的必须那么大并且您的程序不能在堆上分配它,那么您的程序一开始就真的没有业务在这种类型的机器上运行。
你(究竟)想要完成什么?
【讨论】:
我正在使用 ProjectEuler.net 中的问题来学习 C,并且正在实施埃拉托色尼筛算法,所以它必须那么大。 malloc 可以很好地满足我的目的【参考方案7】:你的数组很大。
您的机器或操作系统可能没有或不想分配这么多内存。
如果你绝对需要一个巨大的数组,你可以尝试动态分配它(使用malloc(...)
),但是你有内存泄漏的风险。不要忘记释放内存。
malloc 的优点是它尝试在堆而不是堆栈上分配内存(因此不会出现堆栈溢出)。
您可以检查 malloc 返回的值以查看分配是成功还是失败。 如果失败,只需尝试 malloc 一个较小的数组。
另一种选择是使用可以动态调整大小的不同数据结构(如链表)。此选项是否合适取决于您要如何处理数据。
另一种选择是将内容存储在文件中,即时流式传输数据。这种方法最慢。
如果你想在硬盘上存储,你还不如使用现有的库(用于数据库)
【讨论】:
【参考方案8】:由于 Turbo C/C++ 是 16 位编译器 int 数据类型,因此消耗大约 2 个字节。 2bytes*2000000=40,00,000 bytes=3.8147MB 空间。
函数的自动变量存储在堆栈中,导致堆栈内存溢出。而是使用数据内存 [使用静态或全局变量] 或动态堆内存 [使用 malloc/calloc] 根据处理器内存映射的可用性创建所需的内存。
【讨论】:
以上是关于声明大数组时出现堆栈溢出异常的主要内容,如果未能解决你的问题,请参考以下文章
Expecto FsCheck在生成字符串时出现堆栈溢出异常
使用 dynamic_pointer_cast 时出现异常 0xC0000409(堆栈缓冲区溢出)[关闭]