Domanda difetto di segmentazione: 11


Sto avendo un problema con qualche programma, ho cercato i difetti di segmentazione, non li capisco abbastanza bene, l'unica cosa che so è che presumibilmente sto cercando di accedere ad una memoria che non dovrei. Il problema è che vedo il mio codice e non capisco cosa sto facendo male.

#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 tuo tempo.


35
2017-10-06 19:09


origine


risposte:


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.


66
2017-10-06 19:14



L'array occupa circa 8 GB di memoria (1.000 x 1.000.000 x sizeof (double) 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.

La scrittura di molti dati su 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).

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

Dovresti anche scrivere una funzione per eseguire il calcolo; potresti usare un inline funzione in C99 o successivo (o C ++). La ripetizione nel codice è straziante da guardare.

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

int main(void)

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

Ho quindi creato una seconda copia e creato una funzione inline per eseguire il calcolo ripetuto (e una seconda per eseguire i 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 a 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 RAM 1333 MHz, 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 è dedicato all'I / O del disco. Ho ridotto l'I / O del disco solo all'ultima riga di dati, quindi l'I / O esterno for loop è diventato:

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

i tempi erano notevolmente ridotti e molto più consistenti:

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.


30
2017-10-06 19:18



Su quale sistema stai correndo? Hai accesso a una sorta di debugger (gdb, debugger di Visual Studio, ecc.)?

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

Inoltre, posso raccomandare di sostituire i limiti numerici con le definizioni con nome?

Come tale:

#define DIM1_SZ 1000
#define DIM2_SZ 1000000

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


1
2017-10-06 19:12



Esegui il tuo programma con valgrind di 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.


0
2017-10-06 19:15