2009-05-12 13 views
21

Sto scrivendo un po 'di codice C++ in Linux dove ho dichiarato alcune matrici 2D in questo modo:matrice 2D Grande dà errore di segmentazione

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

Durante la compilazione non ci sono errori. Quando eseguo dice "difetto di segmentazione".

Wen I ridurre le dimensioni dell'array da 5000 a 50, il programma funziona correttamente. Come posso proteggermi da questo problema?

+1

Si dichiara y due volte. Penso che tu voglia x, ye z. –

+2

Domanda StackOverflow classica. :-) – CDR

+0

Per inciso, è un po 'inquietante che consideriamo doppio x [5] [5], ecc. Corretto ma doppio x [5000] [500] sospetto. Era il limite? In un certo senso, una risposta valida sarebbe "non ti preoccupare, il tuo codice è perfettamente corretto secondo lo standard C++". –

risposta

3

La dichiarazione deve essere visualizzata al livello superiore, al di fuori di qualsiasi procedura o metodo.

Di gran lunga il modo più semplice per diagnosticare un segfault in C o C++ di codice è quello di uso valgrind. Se uno dei tuoi array è in errore, valgrind individuerà esattamente dove e come. Se l'errore si trova altrove, lo dirà anche a te.

valgrind può essere utilizzato su qualsiasi binario x86 ma fornirà ulteriori informazioni se si compila con gcc -g.

+0

Mettendolo al livello più alto si userà l'heap. Un altro modo per farlo è usare new o malloc (ricordarsi di liberare memoria in modo appropriato una volta terminato). –

+2

Toplevel lo rende globale (dati non inizializzati, mai ripristinati). Di solito, quando le persone parlano dell'heap, significano allocare dinamicamente. Il problema con malloc è che si perde la notazione array 2D ... –

+0

@Norman Ramsey Agreed, essere in grado di scrivere x [i] [j] invece di x [i * XDIM] + j è un grande vantaggio per la maggior parte non ossessivo programmatori informatici. Sospetto che l'OP sia un fisico. –

14

Questi array sono in pila. Le pile sono di dimensioni abbastanza limitate. Probabilmente esegue in un ... overflow dello stack :)

Se si vuole evitare questo, è necessario metterli sul negozio libera:

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

Ma è meglio iniziare la buona abitudine di utilizzare il contenitori standard, che avvolgono tutto questo per voi:

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

più: anche se la pila si adatta gli array, è ancora bisogno di spazio per le funzioni di mettere le loro cornici su di esso.

+0

Probabilmente dovrebbe leggere: 'double * x = new double [5000 * 500]' con un singolo puntatore *. (Oppure un array 2D 2D realmente più ingombrante assegnato ... –

+1

Una prenotazione sull'uso del vettore sempre: per quanto ho capito, se si esce dalla fine dell'array si assegna solo un array più grande e si copia tutto ciò che potrebbe creare errori sottili e difficili da trovare quando si sta davvero legando lavorare con una matrice di dimensioni fisse. Almeno con un vero array ti segfault se ti allontani rendendo l'errore più facile da catturare. –

+0

@dribeas: grazie. In effetti, gli array multidim non sono i miei preferiti :) – xtofl

1

Mi sembra che tu abbia un overflow onesto per Spolsky!

Prova a compilare il programma con l'opzione -fstack-check di gcc. Se i tuoi array sono troppo grandi per essere allocati nello stack, otterrai un'eccezione StorageError.

Penso che sia una buona scommessa, tuttavia, dato che 5000 * 500 * 3 raddoppia (8 byte ciascuno) arriva a circa 60 mega - nessuna piattaforma ha abbastanza stack per quello. Dovrai allocare i tuoi grossi array sul mucchio.

+0

Quando uso l'opzione -fstack_check in gcc e g ++ dice cc1plus: errore: opzione della riga di comando non riconosciuta "-fstack_check" –

+0

Prova -fstack-check invece di -fstack_check –

+0

double m_x [500000] [500], m_y [500000 ] [500], m_z [500000] [500]; Quando eseguo con solo la statistica (sopra) in main() è in esecuzione. –

58

Se il programma si presenta così ...

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

... poi si traboccano lo stack. Il modo più veloce per risolvere questo problema è aggiungere la parola statica.

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

Il secondo modo più veloce per risolvere questo problema è quello di spostare la dichiarazione dalla funzione:

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

Il terzo modo più veloce per risolvere questo problema è quello di allocare la memoria sul mucchio:

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

Il quarto modo più veloce consiste nell'assegnarli all'heap utilizzando std :: vector.Sono presenti meno righe nel tuo file ma più righe nell'unità di compilazione e devi pensare a un nome significativo per i tipi di vettore derivati ​​o inserirli in uno spazio dei nomi anonimo in modo che non inquinino lo spazio dei nomi globale:

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

il modo quinto più veloce è ripartirli sul mucchio, ma utilizzare modelli così le dimensioni non sono così remote dagli oggetti:

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

il modo più performante è allocare le matrici bidimensionali come uno -array dimensionali e quindi utilizzare l'aritmetica dell'indice.

Tutto ciò presuppone che si abbia un motivo, buono o scarso, per voler creare il proprio meccanismo di array multidimensionale. Se non avete ragione, e si aspettano di utilizzare nuovamente gli array multidimensionali, considerare vivamente di installare una libreria:

+0

Ricontrolla le condizioni. Si assegna 5000 double * ma in seguito ne allocano solo 500 (il ciclo for dovrebbe iterare a 5000) –

+0

Bello! Non pensavo alla statica. Potrebbe notare che questo rende la funzione non rientranti, però! – xtofl

+0

@dribeas grazie per il bug report. –

5

Si consiglia di cercare di utilizzare 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]); 

L'attuale grande pezzo di memoria sarà assegnato sul mucchio e rilascia automaticamente quando necessario.

0

Un'altra soluzione ai precedenti sarebbe eseguire un

ulimit -s stack_area 

per espandere la pila massima.

+0

Come usare ulimit -s stack_area –

+0

Dovresti eseguirlo dalla shell. Altro su uomo ulimit. – Tom

2

Una prenotazione sull'utilizzo sempre del vettore: per quanto ho capito, se si esce dalla fine dell'array si assegna semplicemente un array più grande e si copia tutto ciò che potrebbe creare errori sottili e difficili da trovare quando si è veramente legare per lavorare con una matrice di dimensioni fisse. Almeno con un vero array ti segfault se ti allontani rendendo l'errore più facile da catturare.

#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

Buon punto sul vettore in continua crescita. È solo quando usi push_back, però. Quando lo indicizzi, o esegui un'iterazione oltre il limite, ti imbatterai in un segfault uguale. Non c'è alcuna garanzia di segfault, però: solo quando si usano i segnalini di terra di nessuno intorno ai bordi. – xtofl

+0

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

+0

perché ho scritto l'esempio in C, non C++ –