声明一个大数组时遇到了堆栈溢出异常

53

下面的代码在我的电脑上导致了栈溢出错误

int main(int argc, char* argv[])
{
    int sieve[2000000];
    return 0;
}

我该怎么解决这个问题?我正在使用Turbo C++,但是想保持我的代码仍然是C。

编辑:

谢谢您的建议。上面的代码只是举例,实际上我在一个函数中声明该数组而不是在子主函数中。此外,我需要将数组初始化为零,所以当我搜索malloc时,我发现calloc非常适合我的需求。

Malloc/calloc的优点是允许我使用变量声明大小,而不是在堆栈上分配空间。


13
我读到“堆栈溢出异常”这个词,想着自己...?这个网站有什么问题吗?很明显,我在这里花费的时间太多了 :-/ - David Z
5
我很确定这类问题以前在这个网站上已经出现过,但是搜索"stack overflow"毫无用处。 - Patrick McDonald
4
我认为每个C程序员第一次都会浪费很多时间来解决这个问题。 - MahlerFive
2
Turbo C++是一个16位应用程序,这意味着它使用内存分段,每个段的大小为64KB,因此没有结构可以超过这个数值,并且总内存使用量最大为640KB(使用一些扩展内存管理器可达到1MB或更多)。为什么你需要使用这样一个20多年前的编译器呢? - phuclv
1
到目前为止,希望您已经发现了GCC。它与_Code::Blocks_ IDE一起捆绑出现在许多其他地方。 - ryyker
显示剩余4条评论
8个回答

66

你的数组太大了,无法放入堆栈中,请考虑使用堆:

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

如果您真的想要改变堆栈大小,请阅读这份文件。

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


24
由于这是C语言,您不需要(实际上也不应该)强制转换malloc的返回值。 - aib
1
为什么不对malloc的结果进行强制类型转换?如果你想要对它进行任何操作,难道不需要将其从void*转换为其他类型吗? - user47589
5
你不需要显式地进行类型转换。因为被分配的变量也是指针类型,所以赋值会执行隐式转换。 - jweyrich
4
@Amy 请阅读这篇文章:"Do I cast the result of malloc?"。它解释了为什么在C语言中不需要对malloc(或其他内存分配函数)进行强制类型转换的几个原因。 - WhozCraig
相关:C++规范问答建议在出现相同问题时使用new/delete或std::vector:大数组大小的分段错误 - Peter Cordes

15

有三种方式:

  1. 在堆上分配数组 - 使用malloc(),如其他帖子所建议的。别忘了用free()来释放它(虽然对于main()而言并不那么重要 - 操作系统将会在程序终止时清理内存)。
  2. 在单元级别上声明数组 - 它将被分配到数据段中,并对每个人可见(在声明中添加static将限制该数组的可见性为该单元)。
  3. 将您的数组声明为static - 在这种情况下,它将被分配到数据段中,但仅在main()中可见。

1
我会将它设为静态的:main()只应该被调用一次,所以不会有任何问题;这里也不需要使用malloc()... - Christoph

1

这大约是7MB的堆栈空间。在Visual Studio中,您可以使用/STACK:###,###来反映您想要的大小。如果您真的需要一个巨大的堆栈(可能有很好的理由,比如使用LISP之类的语言:),即使是堆也受到小型分配的限制,强制您使用VirtualAlloc),您还可能希望将PE设置为使用/LARGEADDRESSAAWARE(再次使用Visual Studio的链接器),但这会配置您的PE头文件,以允许您编译的二进制文件在32位地址空间的完整4GB中寻址(如果在WOW64中运行)。如果构建真正巨大的二进制文件,您还通常需要将/bigobj配置为附加的链接器参数。

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


1

使用malloc函数。并且检查返回类型不为空,如果为空,则表示您的系统没有足够的内存来容纳那么多值。


0
你最好将它分配在堆上,而不是栈上。可以这样做:
int main(int argc, char* argv[])
{
    int * sieve;
    sieve = malloc(20000);
    return 0;
}

1
检查筛子是否为NULL。 - paulm
我只是你的筛子 - Zohan - DragonLord
4
我认为你的意思是sieve = malloc(20000 * sizeof *sieve) - 除非你的平台有大小为1的int(即使如此,我也不会将这种假设嵌入代码中)。 - Toby Speight

0
作为 Turbo C/C++ 是 16 位编译器,int 数据类型占用约 2 字节。 2 字节 * 2000000 = 40,00,000 字节 = 3.8147MB 空间。
函数的自动变量存储在堆栈中,这导致堆栈内存溢出。而是应使用数据内存(使用静态或全局变量)或动态堆内存(使用 malloc/calloc)根据处理器内存映射的可用性创建所需的内存。

0

你的数组非常大。

可能是因为你的计算机或操作系统没有足够的内存来分配。


如果你绝对需要一个巨大的数组,你可以尝试动态分配它(使用malloc(...)),但这样你就有可能泄漏内存。不要忘记释放内存。
malloc的优点是它尝试在堆上分配内存而不是栈(因此您不会遇到栈溢出)。
您可以检查malloc返回的值以查看分配是否成功或失败。如果失败,请尝试malloc一个较小的数组。

另一个选择是使用可以在运行时调整大小的不同数据结构(如链表)。这个选项是否好取决于您要对数据做什么。

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

如果您选择在硬盘上进行存储,最好使用现有的库(用于数据库)。


-1

你为什么不能使用alloca()根据对象的实际大小在堆栈帧上分配所需的空间?

如果您这样做,仍然会使堆栈溢出,请将其放入已分配的堆中。我强烈建议不要在main()中将其声明为静态变量并将其放置在数据段中。

如果它确实需要那么大,而您的程序无法在堆上分配它,则您的程序真的没有必要在那种类型的机器上运行。

您究竟想要完成什么任务?


我正在使用ProjectEuler.net上的问题来学习C语言,并实现了埃拉托色尼筛法算法,因此它不必太大。对于我的目的,malloc函数可以很好地工作。 - Patrick McDonald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接