Подтвердить что ты не робот

Большая 2D-матрица дает ошибку сегментации

Я пишу код С++ в Linux, где я объявлял несколько 2D-массивов, например:

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

Во время компиляции ошибка отсутствует. Когда я выполняю это, это говорит о "ошибке сегментации".

Вэнь я уменьшаю размер массива от 5000 до 50, программа работает нормально. Как я могу защитить себя от этой проблемы?

4b9b3361

Ответ 1

Если ваша программа выглядит так...

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 Multimimensional Массив.

  • Скоростным способом является использование Blitz ++.

Ответ 2

Эти массивы находятся в стеке. Стеки довольно ограничены по размеру. Вероятно, вы столкнулись с переполнением стека:)

Если вы хотите этого избежать, вам нужно поместить их в свободный магазин:

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

Но лучше начать хорошую привычку использовать стандартные контейнеры, которые обертывают все это для вас:

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

Плюс: даже если стек подходит для массивов, вам все еще нужно место для функций, чтобы поместить на него свои фреймы.

Ответ 3

Вы можете попробовать и использовать 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]);

Фактический большой кусок памяти будет выделен в куче и автоматически освободится при необходимости.

Ответ 4

Ваше объявление должно отображаться на верхнем уровне, вне любой процедуры или метода.

Далеко самый простой способ диагностики segfault в коде C или С++ - использовать valgrind. Если один из ваших массивов виноват, valgrind точно определит, где и как. Если ошибка лежит в другом месте, это также скажет вам об этом.

valgrind может использоваться в любом двоичном коде x86, но даст дополнительную информацию, если вы скомпилируете gcc -g.

Ответ 5

Оговорка о всегда использовании вектора: насколько я понимаю, если вы выходите из конца массива, он просто выделяет больший массив и копирует все, что может создать тонкие и трудно найти ошибки, когда вы действительно связываете для работы с массивом фиксированного размера. По крайней мере, с реальным массивом вы будете 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;
}

Ответ 6

Похоже на то, что у вас переполнение стека с честным до Spolsky!

Попробуйте выполнить компиляцию вашей программы с помощью опции gcc -fstack-check. Если ваши массивы слишком велики для выделения в стеке, вы получите исключение StorageError.

Я думаю, что это хорошая ставка, однако, поскольку 5000 * 500 * 3 удваивается (по 8 байт каждый), достигает около 60 мегабайт - никакой платформы для этого недостаточно. Вам придется выделять большие массивы в кучу.

Ответ 7

Другим решением предыдущих было бы выполнить

ulimit -s stack_area

чтобы развернуть максимальный стек.