c++ 大2D阵列导致分割错误

ev7lccsx  于 2022-12-20  发布在  其他
关注(0)|答案(8)|浏览(123)

我在Linux中编写了一些C++代码,其中我声明了一些2D数组,如下所示:

double x[5000][500], y[5000][500], z[5000][500];

在编译过程中没有错误。当我执行它时,它说“分段错误”。
当我把数组的大小从5000减到50时,程序运行正常。我如何保护自己不受这个问题的影响?

hpcdzsge

hpcdzsge1#

如果你的程序看起来像这样...

int main(int, char **) {
   double x[5000][500],y[5000][500],z[5000][500];
   // ...
   return 0;
}

...那么堆栈就会溢出。解决这个问题的最快方法是添加单词 static

int main(int, char **) {
   static double x[5000][500],y[5000][500],z[5000][500];
   // ...
   return 0;
}

第二快的解决方法是将声明移出函数:

double x[5000][500],y[5000][500],z[5000][500];
int main(int, char **) {
   // ...
   return 0;
}

第三种最快的解决方法是在堆上分配内存:

int main(int, char **) {
   double **x = new double*[5000];
   double **y = new double*[5000];
   double **z = new double*[5000];
   for (size_t i = 0; i < 5000; i++) {
      x[i] = new double[500];
      y[i] = new double[500];
      z[i] = new double[500];
   }
   // ...
   for (size_t i = 5000; i > 0; ) {
      delete[] z[--i];
      delete[] y[i];
      delete[] x[i];
   }
   delete[] z;
   delete[] y;
   delete[] x;

   return 0;
}

第四种最快的方法是使用std::vector在堆上分配它们,这样文件中的行数会更少,但编译单元中的行数会更多,而且你必须为派生的向量类型想出一个有意义的名称,或者将它们放入一个匿名的名称空间中,这样它们就不会污染全局名称空间:

#include <vector>
using std::vector
namespace { 
  struct Y : public vector<double> { Y() : vector<double>(500) {} };
  struct XY : public vector<Y> { XY() : vector<Y>(5000) {} } ;
}
int main(int, char **) {
  XY x, y, z;
  // ...
  return 0;
}

第五种最快的方法是在堆上分配维度,但是使用模板,这样维度就不会离对象太远:

include <vector>
using namespace std;
namespace {
  template <size_t N>
  struct Y : public vector<double> { Y() : vector<double>(N) {} };
  template <size_t N1, size_t N2>
  struct XY : public vector< Y<N2> > { XY() : vector< Y<N2> > (N1) {} } ;
}
int main(int, char **) {
  XY<5000,500> x, y, z;
  XY<500,50> mini_x, mini_y, mini_z;
  // ...
  return 0;
}

性能最好的方法是将二维数组作为一维数组分配,然后使用索引算法。
以上所有的假设都是假设你有一些理由,不管是好是坏,来创建你自己的多维数组机制。如果你没有理由,并且希望再次使用多维数组,强烈考虑安装一个库:

  • 一种很好地使用STL的方法是使用Boost多维数组。
  • 一个快速的方法是使用 lightning 战++。
y53ybaqx

y53ybaqx2#

这些数组在堆栈上。堆栈的大小相当有限。您可能会遇到...堆栈溢出:)
如果你想避免这种情况,你需要把它们放在免费商店:

double* x =new double[5000*5000];

但是您最好开始养成使用标准容器的好习惯,它会为您 Package 所有这些内容:

std::vector< std::vector<int> > x( std::vector<int>(500), 5000 );

另外:即使堆栈适合数组,您仍然需要为函数提供空间,以便将它们的框架放在堆栈上。

0ejtzxu1

0ejtzxu13#

您可能希望尝试使用Boost.Multi_array

typedef boost::multi_array<double, 2> Double2d;
Double2d x(boost::extents[5000][500]);
Double2d y(boost::extents[5000][500]);
Double2d z(boost::extents[5000][500]);

实际的大内存块将在堆上分配,并在必要时自动释放。

niknxzdl

niknxzdl4#

您的声明应该出现在顶层,在任何过程或方法之外.
到目前为止,在C或C++代码中诊断segfault最简单的方法是使用valgrind。如果你的一个数组出现了错误,valgrind会精确地指出错误的位置和方式。如果错误发生在其他地方,它也会告诉你。
valgrind可以在任何x86二进制文件上使用,但如果使用gcc -g编译,将给予更多信息。

ifsvaxew

ifsvaxew5#

关于始终使用向量的一个保留意见是:据我所知,如果你走到数组的末尾,它只会分配一个更大的数组,并复制所有的内容,当你真正使用固定大小的数组时,这可能会产生微妙的、难以发现的错误。至少对于一个真正的数组,如果你走到末尾,你会出现segfault,从而使错误更容易捕捉。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {

typedef double (*array5k_t)[5000];

array5k_t array5k = calloc(5000, sizeof(double)*5000);

// should generate segfault error
array5k[5000][5001] = 10;

return 0;
}
yquaqz18

yquaqz186#

在我看来,你有一个诚实的Spolsky堆栈溢出!
试着用gcc的-fstack-check选项编译你的程序,如果你的数组太大而不能在堆栈上分配,你会得到一个StorageError异常。
我认为这是一个很好的选择,因为50005003个双精度数(每个8字节)大约有60兆--没有平台有足够的堆栈,你必须在堆上分配你的大数组。

1l5u6lss

1l5u6lss7#

前一个问题的另一个解决方案是执行

ulimit -s stack_area

以扩展最大堆栈。

zaq34kh6

zaq34kh68#

您可能想尝试使用Multi库来实现多维数组(C++17)。

#include<multi/array.hpp>
#include<cassert>

namespace multi = boost::multi;

int main() {
    using Double2D = multi::array<double, 2>;
    Double2D X({5000, 500}, 999.0);
    Double2D Y({5000, 500});
    Double2D Z({5000, 500});

    assert( X.size() == 5000 );

    auto [m, n] = X.extensions();
    assert( m == 5000 );
    assert( n ==  500 );

    Y = X;
    assert( Y[0][0] == 999.0 );
}

https://godbolt.org/z/rh5M463Y1
类似于Boost.MultiArray库(另一个答案),在堆中分配内存而不是使用(溢出)堆栈。它提供了其他特性,如赋值和迭代。

相关问题