2009-05-12 4 views
21

Я пишу некоторые C++ кода в Linux, где я объявленный несколько 2D массивов, как так:Большого 2D массив дает ошибку сегментации

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

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

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

+1

Вы объявляете y дважды. Я думаю, вам нужны x, y и z. –

+2

Классический вопрос о StackOverflow. :-) – CDR

+0

Как в сторону, это немного беспокоит, что мы рассматриваем double x [5] [5] и т. Д. Правильно, но double x [5000] [500] подозрительно. Был ли предел? В некотором смысле, действительный ответ будет «не волнуйтесь, ваш код совершенно корректен в соответствии со стандартом C++». –

ответ

3

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

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

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

+0

Поместите его на верхний уровень, чтобы он использовал кучу. Другой способ сделать это - использовать новый или malloc (помните, чтобы при необходимости сохранить свободную память). –

+2

Toplevel делает его глобальным (неинициализированные данные, никогда не восстанавливаются). Обычно, когда люди говорят о куче, они подразумевают динамическое распределение. Проблема с malloc заключается в том, что вы потеряете двухмерную нотацию массива ... –

+0

@Norman Ramsey Согласен, имея возможность писать x [i] [j] вместо x [i * XDIM] + j является большим преимуществом для большинства не-обсессивных кодеры. Я подозреваю, что ОП - физик. –

14

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

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

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

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

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

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

+0

Это должно, вероятно, читать: «double * x = new double [5000 * 500]» с одним указателем *. (Или еще более громоздкий реальный 2D динамически распределенный массив ...) –

+1

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

+0

@dribeas: спасибо. Действительно, многодисковые массивы не являются моими фаворитами :) – xtofl

1

Выглядит так, как будто у вас есть переполнение стека с честным до Spolsky!

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

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

+0

Когда я использую параметр -fstack_check как в gcc, так и в g ++, он сообщает cc1plus: ошибка: непризнанная опция командной строки «-fstack_check» – 2009-05-12 04:38:15

+0

Try -fstack-check вместо -fstack_check –

+0

double m_x [500000] [500], m_y [500000 ] [500], m_z [500000] [500]; Когда я выполняю только с статусом (выше) в main(), он выполняется. – 2009-05-12 05:36:37

58

Если ваша программа выглядит следующим образом ...

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 Multidimensional Array.

  • Скоростной способ использовать Blitz++.

+0

Переустановите условия. Вы выделяете 5000 double *, но затем выделяете только 500 из них (цикл for должен перебираться до 5000) –

+0

Nice one! Я не думал о статике. Можете заметить, что это делает функцию не реентерабельной, хотя! – xtofl

+0

@dribeas благодарит за сообщение об ошибке. –

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]); 

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

0

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

ulimit -s stack_area 

расширить максимальный стек.

+0

Как использовать ulimit -s stack_area – 2009-05-12 05:28:22

+0

Вы должны выполнить его из оболочки. Подробнее о мужчине улимите. – Tom

2

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

Хороший вопрос о постоянно растущем векторе. Однако, только когда вы используете push_back. Когда вы индексируете его или перебираете по краю, вы будете работать в segfault точно так же. Однако нет никакой гарантии для segfault: только при использовании маркеров без земли по краям. – xtofl

+0

почему бы не «array5k_t array5k = новый двойной [5000] [5000]; – newacct

+0

, потому что я написал пример в C, а не C++ –

Смежные вопросы