大数组大小的分段错误

Posted

技术标签:

【中文标题】大数组大小的分段错误【英文标题】:Segmentation fault on large array sizes 【发布时间】:2010-12-23 07:08:52 【问题描述】:

以下代码在 2Gb 机器上运行时出现分段错误,但在 4GB 机器上运行。

int main()

   int c[1000000];
   cout << "done\n";
   return 0;

数组的大小只有 4Mb。 c++中可以使用的数组大小有限制吗?

【问题讨论】:

【参考方案1】:

在 C 或 C++ 中,本地对象通常分配在堆栈上。您在堆栈上分配了一个大数组,超出了堆栈可以处理的范围,因此您得到了 ***.

不要在堆栈上本地分配它,而是使用其他地方。这可以通过使对象 global 或将其分配到全局 heap 来实现。全局变量很好,如果您不使用来自任何其他编译单元的 。为确保这不会意外发生,请添加静态存储说明符,否则只需使用堆。

这将在 BSS 段中分配,该段是堆的一部分。由于它在静态存储中,因此如果您不另外指定,则初始化为零,这与包括数组在内的局部变量(自动存储)不同。

static int c[1000000];
int main()

   cout << "done\n";
   return 0;

非零初始化器将使编译器在 DATA 段中分配,这也是堆的一部分。 (并且数组初始值设定项的所有数据都将占用可执行文件中的空间,包括所有隐式尾随零,而不仅仅是 BSS 中零初始化的大小)

int c[1000000] = 1, 2, 3;
int main()

   cout << "done\n";
   return 0;

这将分配在堆中的某个未指定位置:

int main()

   int* c = new int[1000000];  // size can be a variable, unlike with static storage
   cout << "done\n";
   delete[] c;            // dynamic storage needs manual freeing
   return 0;

【讨论】:

如果你使用第三种模式,在堆上分配,不要忘记在某个阶段删除[]指针,否则你会泄漏内存。或者查看智能指针。 @meowsqueak 当然,使用new 分配的任何地方都使用delete 是一种很好的做法。但是,如果您确定只分配一次内存(如在 main 中),则严格不需要 - 即使没有明确的 delete,也可以保证在 main 退出时释放内存。 'at'drhirsch(你怎么做一个at-character?) - 是的,公平的评论。由于 OP 对语言来说似乎是新的,我只是想确保他们以及其他任何看到你的好答案的人都知道如果普遍使用第三个选项的含义。【参考方案2】:

您可能只是在这里遇到了堆栈溢出。数组太大,无法放入程序的堆栈区域;大多数主流桌面/服务器操作系统上的用户空间代码的堆栈增长限制通常为 8 MiB 或 1 MiB。 (普通的 C++ 实现使用 asm 堆栈进行自动存储,即非static 局部变量数组。这使得在函数返回或异常通过它们传播时可以免费释放它们。)

如果你动态分配数组你应该没问题,假设你的机器有足够的内存。

int* array = new int[1000000];    // may throw std::bad_alloc

但请记住,这将需要您手动delete[] 数组以避免内存泄漏,即使您的函数通过异常退出。在现代 C++ 中强烈建议不要手动新建/删除,更喜欢 RAII。


更好的解决方案是使用std::vector&lt;int&gt; array (cppreference)。如果您知道它会增长多大,您可以为 1000000 个元素保留空间。甚至 resize 它默认构造它们(即零初始化内存,与声明没有初始化器的普通 C 样式数组不同),如 std::vector&lt;int&gt; array(1000000)

std::vector 对象超出范围时,它的析构函数将为您释放存储空间,即使这是通过父函数捕获的子函数中的异常发生的。

【讨论】:

谢谢你的回答,但你能解释一下为什么数组分配在堆栈上,为什么不在主程序内存中。 给定的代码在堆栈上分配,因为它在编译时被指定为具有恒定数量元素的数组。值仅与 malloc、new 等一起放在堆上。 所有自动变量都分配在堆栈上。如果您查看 disasseble,您将看到从堆栈指针中减去局部变量的大小。当您调用 malloc 或 calloc 或任何内存功能时,这些功能会找到足够大的内存块来满足您的要求。 @Charles 为什么我们可以从堆而不是堆栈分配更多内存?据我了解,堆栈和堆在内存中分配的地址空间中的移动方向相反。 @saurabhagarwal 堆没有移动。它甚至不是一个连续的内存区域。分配器只返回一个符合您大小要求的空闲内存块What and where are the stack and heap?【参考方案3】:

还有另一种对我有用的方法! 你可以通过改变数组的数据类型来减小数组的大小:

    int main()
        
        short c[1000000];
        cout << "done\n";
        return 0;
        

  int main() 
  
      unsigned short c[1000000];
      cout << "done\n";
      return 0;
  

【讨论】:

【参考方案4】:

您的普通数组是在堆栈中分配的,并且堆栈被限制为几兆字节,因此您的程序会发生堆栈溢出并崩溃。

最好是使用堆分配的基于std::vector 的数组,它可以增长到几乎整个内存的大小,而不是普通数组。

Try it online!

#include <vector>
#include <iostream>

int main() 
   std::vector<int> c(1000000);
   std::cout << "done\n";
   return 0;

然后您可以像往常一样访问数组的元素c[i] 和/或获取其大小c.size()int 元素的数量)。

如果您想要具有固定维度的多维数组,请同时使用std::vector 和std::array,如下所示:

Try it online!

#include <vector>
#include <array>
#include <iostream>

int main() 
   std::vector<std::array<std::array<int, 123>, 456>> c(100);
   std::cout << "done\n";
   return 0;

在上面的示例中,您获得的行为与分配普通数组 int c[100][456][123]; 几乎相同(除了向量在堆而不是堆栈上分配),您可以像在普通数组中一样访问 c[10][20][30] 元素。上面的这个例子还在堆上分配数组,这意味着您可以将数组大小增加到整个内存大小,而不受堆栈大小的限制。

要获取指向向量中第一个元素的指针,请使用&amp;c[0] 或仅使用c.data()

【讨论】:

【参考方案5】:

此外,如果您在大多数 UNIX 和 Linux 系统中运行,您可以通过以下命令临时增加堆栈大小:

ulimit -s unlimited

但是要小心,内存是一种有限的资源,而且能力越大,责任越大:)

【讨论】:

这是解决方案,但我建议大家在删除程序堆栈大小的默认限制时要格外小心。您不仅会经历严重的性能下降,而且您的系统可能会崩溃。例如,我尝试在具有 4GB RAM 的机器上使用快速排序对包含 16 000 000 个整数元素的数组进行排序,但我的系统几乎被杀死。哈哈 @rbaleksandar 我认为你 ~ 16MB 的程序几乎会杀死你的机器,因为你正在处理数组的多个副本(可能每个函数调用一个?)尝试更多的内存感知实现;) 我很确定数组处理没问题,因为我通过引用而不是值传递。冒泡排序也会发生同样的事情。见鬼,即使我的快速排序实现很糟糕,冒泡排序也是你不可能错误地实现的。哈哈 大声笑你可以尝试基数排序,或者干脆使用 std::sort :) 没有机会。这是一个实验室任务。 :D【参考方案6】:

因为您将数组存储在堆栈中。您应该将其存储在堆中。看this link了解堆和栈的概念。

【讨论】:

【参考方案7】:

在这种情况下,您的数组正在堆栈上分配,尝试使用 alloc 分配相同大小的数组。

【讨论】:

以上是关于大数组大小的分段错误的主要内容,如果未能解决你的问题,请参考以下文章

C ++ 11分段错误试图将数组(<算法>)动态复制到向量中

大小为 500000 的部分排序数组的快速排序分段错误

测试用例中的分段错误

尝试声明大数组时出现分段错误和核心转储[重复]

初始化数组时出现段错误

带有clang的C大数组中的奇怪分段错误