2012-10-06 4 views
29

Sto riscontrando un problema con qualche programma, ho cercato degli errori di segmentazione, non li capisco abbastanza bene, l'unica cosa che so è che presumibilmente sto cercando di accedere ad alcuni ricordi non dovrebbe. Il problema è che vedo il mio codice e non capisco cosa sto facendo male.errore di segmentazione: 11

#include<stdio.h> 
#include<math.h> 
#include<stdlib.h> 

#define lambda 2.0 
#define g  1.0 
#define Lx  100 
#define F0  1.0 
#define Tf  10 
#define h  0.1 
#define e  0.00001 

FILE *file; 

double F[1000][1000000]; 

void Inicio(double D[1000][1000000]) { 
int i; 
for (i=399; i<600; i++) { 
    D[i][0]=F0; 
} 
} 

void Iteration (double A[1000][1000000]) { 
long int i,k; 
for (i=1; i<1000000; i++) { 
    A[0][i]= A[0][i-1] + e/(h*h*h*h)*g*g*(A[2][i-1] - 4.0*A[1][i-1] + 6.0*A[0][i-1]-4.0*A[998][i-1] + A[997][i-1]) + 2.0*g*e/(h*h)*(A[1][i-1] - 2*A[0][i-1] + A[998][i-1]) + e*A[0][i-1]*(lambda-A[0][i-1]*A[0][i-1]); 
    A[1][i]= A[1][i-1] + e/(h*h*h*h)*g*g*(A[3][i-1] - 4.0*A[2][i-1] + 6.0*A[1][i-1]-4.0*A[0][i-1] + A[998][i-1]) + 2.0*g*e/(h*h)*(A[2][i-1] - 2*A[1][i-1] + A[0][i-1]) + e*A[1][i-1]*(lambda-A[1][i-1]*A[1][i-1]); 
    for (k=2; k<997; k++) { 
     A[k][i]= A[k][i-1] + e/(h*h*h*h)*g*g*(A[k+2][i-1] - 4.0*A[k+1][i-1] + 6.0*A[k][i-1]-4.0*A[k-1][i-1] + A[k-2][i-1]) + 2.0*g*e/(h*h)*(A[k+1][i-1] - 2*A[k][i-1] + A[k-1][i-1]) + e*A[k][i-1]*(lambda-A[k][i-1]*A[k][i-1]); 
    } 
    A[997][i] = A[997][i-1] + e/(h*h*h*h)*g*g*(A[0][i-1] - 4*A[998][i-1] + 6*A[997][i-1] - 4*A[996][i-1] + A[995][i-1]) + 2.0*g*e/(h*h)*(A[998][i-1] - 2*A[997][i-1] + A[996][i-1]) + e*A[997][i-1]*(lambda-A[997][i-1]*A[997][i-1]); 
    A[998][i] = A[998][i-1] + e/(h*h*h*h)*g*g*(A[1][i-1] - 4*A[0][i-1] + 6*A[998][i-1] - 4*A[997][i-1] + A[996][i-1]) + 2.0*g*e/(h*h)*(A[0][i-1] - 2*A[998][i-1] + A[997][i-1]) + e*A[998][i-1]*(lambda-A[998][i-1]*A[998][i-1]); 
    A[999][i]=A[0][i]; 
} 
} 

main() { 
long int i,j; 
Inicio(F); 
Iteration(F); 
file = fopen("P1.txt","wt"); 
for (i=0; i<1000000; i++) { 
    for (j=0; j<1000; j++) { 
     fprintf(file,"%lf \t %.4f \t %lf\n", 1.0*j/10.0, 1.0*i, F[j][i]); 
    } 
} 
fclose(file); 
} 

Grazie per il vostro tempo.

+0

A che punto si verifica il segfault? –

+1

^Provare a utilizzare Valgrind per ottenere il numero di riga in cui si verifica il segfault. In genere è abbastanza ovvio una volta che l'hai ristretto a una riga - in caso contrario, pubblica la linea e possiamo aiutarti. –

+3

visto il tuo codice, in primo luogo il tuo compilatore deve essere andato errore di segmentazione ... – perilbrain

risposta

62

Questa dichiarazione:

double F[1000][1000000]; 

occuperebbe 8 * 1000 * 1000000 byte su un tipico sistema x86. Questo è circa 7,45 GB. È probabile che il sistema stia esaurendo la memoria quando si tenta di eseguire il codice, il che si traduce in un errore di segmentazione.

+0

grazie, è andata così. – Ariaramnes

1

Quale sistema stai utilizzando? Hai accesso a una sorta di debugger (gdb, debugger di Visual Studio, ecc.)?

Questo ci darebbe informazioni preziose, come la riga di codice in cui il programma si blocca ... Inoltre, la quantità di memoria potrebbe essere proibitiva.

Inoltre, si consiglia di sostituire i limiti numerici con le definizioni denominate?

Come tale:

#define DIM1_SZ 1000 
#define DIM2_SZ 1000000 

utilizzare tali ogni volta che si desidera fare riferimento ai limiti di dimensione dell'array. Aiuterà ad evitare errori di battitura.

0

Eseguire il programma con valgrind collegato a efence. Questo ti dirà dove il puntatore viene sottoposto a dereferenziazione e molto probabilmente risolverà il tuo problema se correggi tutti gli errori che ti dicono.

29

L'array occupa circa 8 GB di memoria (1.000 x 1.000.000 x sizeof (doppio) byte). Questo potrebbe essere un fattore nel tuo problema. È una variabile globale piuttosto che una variabile stack, quindi potresti essere OK, ma qui stai spingendo i limiti.

Scrivere tanti dati in un file richiederà un po 'di tempo.

Non si controlla che il file sia stato aperto correttamente, il che potrebbe essere anche fonte di problemi (in caso contrario, un errore di segmentazione è molto probabile).

È davvero necessario introdurre alcune costanti con nome per 1.000 e 1.000.000; cosa rappresentano?

Si dovrebbe anche scrivere una funzione per eseguire il calcolo; è possibile utilizzare una funzione inline in C99 o successiva (o C++). La ripetizione nel codice è straziante da guardare.

Si dovrebbe anche usare C99 notazione per main(), con il tipo di ritorno esplicito (e preferibilmente void per la lista degli argomenti quando non si utilizza argc o argv):

int main(void) 

Per curiosità di inattività , Ho preso una copia del tuo codice, cambiato tutte le occorrenze da 1000 a ROWS, tutte le occorrenze da 1000000 a COLS, quindi ho creato enum { ROWS = 1000, COLS = 10000 }; (riducendo quindi la dimensione del problema di un fattore di 100).Ho apportato alcune modifiche minori in modo da compilare in modo pulito sotto il mio set preferito di opzioni di compilazione (niente di serio: static davanti alle funzioni e l'array principale; file diventa locale a main; errore controllare il fopen(), ecc.).

Ho quindi creato una seconda copia e creato una funzione inline per eseguire il calcolo ripetuto, (e una seconda per eseguire calcoli in pedice). Ciò significa che l'espressione mostruosa viene scritta solo una volta - cosa altamente auspicabile in quanto garantisce coerenza.

#include <stdio.h> 

#define lambda 2.0 
#define g  1.0 
#define F0  1.0 
#define h  0.1 
#define e  0.00001 

enum { ROWS = 1000, COLS = 10000 }; 

static double F[ROWS][COLS]; 

static void Inicio(double D[ROWS][COLS]) 
{ 
    for (int i = 399; i < 600; i++) // Magic numbers!! 
     D[i][0] = F0; 
} 

enum { R = ROWS - 1 }; 

static inline int ko(int k, int n) 
{ 
    int rv = k + n; 
    if (rv >= R) 
     rv -= R; 
    else if (rv < 0) 
     rv += R; 
    return(rv); 
} 

static inline void calculate_value(int i, int k, double A[ROWS][COLS]) 
{ 
    int ks2 = ko(k, -2); 
    int ks1 = ko(k, -1); 
    int kp1 = ko(k, +1); 
    int kp2 = ko(k, +2); 

    A[k][i] = A[k][i-1] 
      + e/(h*h*h*h) * g*g * (A[kp2][i-1] - 4.0*A[kp1][i-1] + 6.0*A[k][i-1] - 4.0*A[ks1][i-1] + A[ks2][i-1]) 
      + 2.0*g*e/(h*h) * (A[kp1][i-1] - 2*A[k][i-1] + A[ks1][i-1]) 
      + e * A[k][i-1] * (lambda - A[k][i-1] * A[k][i-1]); 
} 

static void Iteration(double A[ROWS][COLS]) 
{ 
    for (int i = 1; i < COLS; i++) 
    { 
     for (int k = 0; k < R; k++) 
      calculate_value(i, k, A); 
     A[999][i] = A[0][i]; 
    } 
} 

int main(void) 
{ 
    FILE *file = fopen("P2.txt","wt"); 
    if (file == 0) 
     return(1); 
    Inicio(F); 
    Iteration(F); 
    for (int i = 0; i < COLS; i++) 
    { 
     for (int j = 0; j < ROWS; j++) 
     { 
      fprintf(file,"%lf \t %.4f \t %lf\n", 1.0*j/10.0, 1.0*i, F[j][i]); 
     } 
    } 
    fclose(file); 
    return(0); 
} 

Questo programma scrive P2.txt invece di P1.txt. Ho eseguito entrambi i programmi e ho confrontato i file di output; l'output era identico. Quando eseguivo i programmi su una macchina in gran parte inattiva (MacBook Pro, Intel Core i7 da 2,3 GHz, 16 GB 1333 MHz RAM, Mac OS X 10.7.5, GCC 4.7.1), ho ottenuto un tempismo ragionevole ma non del tutto coerente:

Original Modified 
6.334s  6.367s 
6.241s  6.231s 
6.315s  10.778s 
6.378s  6.320s 
6.388s  6.293s 
6.285s  6.268s 
6.387s  10.954s 
6.377s  6.227s 
8.888s  6.347s 
6.304s  6.286s 
6.258s  10.302s 
6.975s  6.260s 
6.663s  6.847s 
6.359s  6.313s 
6.344s  6.335s 
7.762s  6.533s 
6.310s  9.418s 
8.972s  6.370s 
6.383s  6.357s 

Tuttavia, quasi tutto il tempo è trascorso sull'I/O del disco. Ho ridotto il disco I/O a poco l'ultima riga di dati, in modo che il ciclo esterno di I/O for diventato:

for (int i = COLS - 1; i < COLS; i++) 

gli orari erano notevolmente ridotto e molto molto più coerente:

Original Modified 
0.168s  0.165s 
0.145s  0.165s 
0.165s  0.166s 
0.164s  0.163s 
0.151s  0.151s 
0.148s  0.153s 
0.152s  0.171s 
0.165s  0.165s 
0.173s  0.176s 
0.171s  0.165s 
0.151s  0.169s 

La semplificazione nel codice di avere l'espressione orribile scritta solo una volta è molto utile, mi sembra. Avrei sicuramente preferito mantenere quel programma piuttosto che l'originale.