2009-05-12 11 views
21

estoy escribiendo algo de código C++ en Linux donde me he dado un par de matrices 2D, así:matriz 2D grande da fallo de segmentación

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

Durante la compilación no hay ningún error. Cuando lo ejecuto dice "falla de segmentación".

Wen Reduzco el tamaño de la matriz de 5000 a 50, el programa funciona bien. ¿Cómo puedo protegerme de este problema?

+1

Lo declararás dos veces. Creo que quieres x, y y z. –

+2

Pregunta clásica de StackOverflow. :-) – CDR

+0

Por un lado, es un poco molesto que consideremos el doble x [5] [5], etc. correcto pero el doble x [5000] [500] sospechoso. ¿Era el límite? En cierto sentido, una respuesta válida sería "no se preocupe, su código es perfectamente correcto según el estándar de C++". –

Respuesta

3

Su declaración debe aparecer en el nivel superior, fuera de cualquier procedimiento o método.

Con mucho, la forma más fácil de diagnosticar una violación de segmento en C o C++ código es uso valgrind. Si una de tus matrices tiene la culpa, valgrind identificará exactamente dónde y cómo. Si la falla está en otra parte, también te dirá eso.

valgrind se puede utilizar en cualquier binario x86 pero dará más información si compila con gcc -g.

+0

Ponerlo en el nivel superior hará que use el montón. Otra forma de hacerlo es usar new o malloc (recuerde liberar memoria de forma apropiada cuando termine). –

+2

Toplevel lo hace global (datos no inicializados, nunca recuperados). Por lo general, cuando las personas hablan sobre el montón, se refieren a asignaciones dinámicas. El problema con malloc es que pierdes la notación de arreglo 2D ... –

+0

@Norman Ramsey De acuerdo, ser capaz de escribir x [i] [j] en lugar de x [i * XDIM] + j es una gran ventaja para la mayoría de los no obsesivos codificadores. Sospecho que el OP es un físico. –

14

Estas matrices están en la pila. Las pilas tienen un tamaño bastante limitado. Probablemente se encuentra con un desbordamiento de pila ... :)

Si se quiere evitar esto, es necesario ponerlos en la tienda libre:

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

Pero es mejor empezar el buen hábito de usar el contenedores estándar, que envuelven todo esto para usted:

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

Plus: incluso si la pila se ajusta a las matrices, todavía se necesitan espacio para las funciones para poner sus marcos en él.

+0

Probablemente debería decir: 'doble * x = nuevo doble [5000 * 500]' con un solo puntero *. (O bien, una matriz 2D dinámicamente asignada real más engorrosa ...) –

+1

Una reserva de que siempre usando vectores: por lo que yo entiendo, si usted camina fuera de la final de la matriz que sólo asigna una matriz y copias grande todo sobre lo que podría crear sutiles y difíciles de encontrar errores cuando se está realmente atar a trabajar con una matriz de tamaño fijo. Al menos con una matriz real, segmentará la falla si sale del final haciendo que el error sea más fácil de atrapar. –

+0

@dribeas: gracias. De hecho, las matrices multidim no son mis favoritas :) – xtofl

1

¡Me parece que tiene un desbordamiento de pila honesto a Spolsky!

Intente compilar su programa con la opción gcc -fstack-check. Si sus matrices son demasiado grandes para asignarlas en la pila, obtendrá una excepción StorageError.

Creo que es una buena apuesta, sin embargo, como 5000 * 500 * 3 dobles (8 bytes cada uno) llega a alrededor de 60 megas, ninguna plataforma tiene suficiente pila para eso. Tendrás que asignar tus matrices grandes en el montón.

+0

Cuando uso la opción -fstack_check en gcc yg ++, dice cc1plus: error: opción de línea de comando no reconocida "-fstack_check" –

+0

Pruebe -fstack-check en lugar de -fstack_check –

+0

double m_x [500000] [500], m_y [500000 ] [500], m_z [500000] [500]; Cuando ejecuto solo la declaración (arriba) en main() se está ejecutando. –

58

Si el programa se ve así ...

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

... entonces se desbordan la pila. La forma más rápida de arreglar esto es agregar la palabra estática.

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

La segunda forma más rápida de solucionar este problema es mover la declaración de la función:

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

La tercera forma más rápida de solucionar este problema es asignar la memoria en el montón:

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

La cuarta forma más rápida es asignarlos en el montón usando std :: vector.Hay menos líneas en su archivo pero más líneas en la unidad de compilación, y debe pensar en un nombre significativo para sus tipos de vectores derivados o incluirlos en un espacio de nombres anónimo para que no contaminen el espacio de nombres global:

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

la forma quinto mejor es asignar ellas en el montón, pero el uso de las plantillas por lo que las dimensiones no son tan alejado de los objetos:

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

la forma mas potente es la asignación de las matrices bidimensionales como uno matrices tridimensionales, y luego usar aritmética de índice.

Todo lo anterior supone que tiene alguna razón, una buena o una mala, para querer crear su propio mecanismo de matriz multidimensional. Si no tiene la razón, y espera utilizar matrices multidimensionales de nuevo, considerar seriamente la posibilidad de instalar una biblioteca:

+0

condiciones vuelva a comprobar. Asignas 5000 double * pero luego solo asignas 500 de ellos (el ciclo for debe iterar a 5000) –

+0

¡Bonito! No pensé en la estática. Sin embargo, ¡podría hacer que la función no sea reentrante! – xtofl

+0

@dribeas gracias por el informe de error. –

5

Es posible que desee para tratar de utilizar 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]); 

El gran trozo de memoria real se asignará en el montón y desasigna automáticamente cuando es necesario.

0

Otra solución a los anteriores habría de ejecutar un

ulimit -s stack_area 

para expandir la pila máxima.

+0

Cómo utilizar ulimit -s stack_area –

+0

debe ejecutar desde el shell. Más sobre el hombre ulimit. – Tom

2

Una reserva acerca de usar siempre el vector: hasta donde yo lo entiendo, si se aleja del final de la matriz solo asigna una matriz más grande y copia todo lo que podría crear errores sutiles y difíciles de encontrar cuando realmente está atar para trabajar con una matriz de tamaño fijo. Al menos con una matriz real, segmentará la falla si sale del final haciendo que el error sea más fácil de atrapar.

#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

Buen punto sobre el vector en constante crecimiento. Sin embargo, eso es solo cuando usas push_back. Cuando lo indices, o iteres sobre el borde, te toparás con un segfault igual. No hay garantía de que segfault, sin embargo: sólo cuando se utilizan marcadores de nadie en tierra alrededor de los bordes. – xtofl

+0

por qué no "array5k_t array5k = new double [5000] [5000];" – newacct

+0

porque escribí el ejemplo en C, C++ no –

Cuestiones relacionadas