31

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

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

在编译过程中没有错误。当我执行时,它说“分段错误”。

文我把数组的大小从5000减少到50,程序运行正常。我怎样才能保护自己免受这个问题的影响?

4

7 回答 7

76

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

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;
}

最高效的方法是将二维数组分配为一维数组,然后使用索引算法。

以上所有假设你有一些理由,一个好的或一个糟糕的,想要制作你自己的多维数组机制。如果你没有理由,并且希望再次使用多维数组,强烈考虑安装一个库:

于 2009-05-12T04:47:44.397 回答
18

这些数组在堆栈上。堆栈的大小非常有限。您可能会遇到...堆栈溢出:)

如果你想避免这种情况,你需要把它们放在免费商店:

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

但是你最好养成使用标准容器的好习惯,它为你包装了所有这些:

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

另外:即使堆栈适合数组,您仍然需要空间来放置函数的框架。

于 2009-05-12T04:28:30.840 回答
5

您可能想尝试使用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]);

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

于 2009-05-12T05:03:50.690 回答
4

你的声明应该出现在顶层,在任何过程或方法之外。

到目前为止,诊断C 或 C++ 代码中的段错误的最简单方法是使用valgrind。如果您的一个阵列出现故障,valgrind 将准确指出在哪里以及如何发生。如果故障出在其他地方,它也会告诉你。

valgrind 可用于任何 x86 二进制文件,但如果使用gcc -g.

于 2009-05-12T04:27:30.140 回答
2

关于始终使用向量的一项保留:据我了解,如果您离开数组的末尾,它只会分配一个更大的数组并复制所有内容,当您真正想要使用时,可能会产生微妙且难以发现的错误一个固定大小的数组。至少对于一个真正的数组,如果你离开最后,你会出现段错误,从而使错误更容易被发现。

#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;
}
于 2009-05-12T06:52:06.003 回答
1

在我看来,你有一个诚实的 Spolsky 堆栈溢出!

尝试使用 gcc 的 -fstack-check 选项编译您的程序。如果您的数组太大而无法在堆栈上分配,您将收到 StorageError 异常。

不过,我认为这是一个不错的选择,因为 5000*500*3 双倍(每个 8 字节)达到大约 60 兆 - 没有平台有足够的堆栈。你必须在堆上分配你的大数组。

于 2009-05-12T04:31:08.857 回答
0

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

ulimit -s stack_area

扩展最大堆栈。

于 2009-05-12T05:03:56.337 回答