2012-10-06 13 views
29

Ich habe ein Problem mit einem Programm, ich habe nach Segmentierungsfehlern gesucht, indem ich sie nicht gut verstehe, das einzige, was ich weiß ist, dass ich vermutlich auf etwas Speicher I zugreifen möchte sollte nicht. Das Problem ist, dass ich meinen Code sehe und nicht verstehe, was ich falsch mache.Segmentierungsfehler: 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); 
} 

Danke für Ihre Zeit.

+0

An welchem ​​Punkt tritt der Segfault auf? –

+1

^Verwenden Sie Valgrind, um die Zeilennummer zu erhalten, an der der Segfault auftritt. Es ist in der Regel ziemlich offensichtlich, wenn Sie es auf eine Zeile eingegrenzt haben - wenn nicht, geben Sie an, welche Zeile es ist und wir können Ihnen helfen. –

+3

Ihren Code zu sehen, zuerst Ihr Compiler muss Segmentierung Fehler gegangen sein ... – perilbrain

Antwort

62

Diese Erklärung:

double F[1000][1000000]; 

besetzen würde 8 * 1000 * 1000000 Bytes auf einem typischen x86-System. Das sind ungefähr 7,45 GB. Es besteht die Möglichkeit, dass Ihrem System beim Ausführen des Codes nicht genügend Arbeitsspeicher zur Verfügung steht, was zu einem Segmentierungsfehler führt.

+0

danke, das war es. – Ariaramnes

1

In welchem ​​System laufen Sie? Haben Sie Zugriff auf eine Art Debugger (gdb, Debugger von Visual Studio usw.)?

Das würde uns wertvolle Informationen geben, wie die Codezeile, wo das Programm abstürzt ... Auch die Menge an Speicher kann prohibitiv sein.

Kann ich zusätzlich empfehlen, dass Sie die numerischen Grenzwerte durch benannte Definitionen ersetzen?

Als solche:

#define DIM1_SZ 1000 
#define DIM2_SZ 1000000 

Verwenden diejenigen, wann immer Sie wollen auf die Array-Dimension Grenzen verweisen. Es hilft Tippfehler zu vermeiden.

0

Führen Sie Ihr Programm mit valgrind von verbunden mit efence. Das wird dir sagen, wo der Zeiger dereferenziert wird und höchstwahrscheinlich dein Problem beheben, wenn du alle Fehler behebst, von denen sie dir erzählen.

29

Ihr Array belegt etwa 8 GB Speicher (1.000 x 1.000.000 x Größe von (doppelten) Bytes). Das könnte ein Faktor für Ihr Problem sein. Es ist eine globale Variable und keine Stapelvariable. Sie können also in Ordnung sein, aber Sie stoßen hier an Grenzen.

Das Schreiben von so vielen Daten in eine Datei wird eine Weile dauern.

Sie überprüfen nicht, ob die Datei erfolgreich geöffnet wurde, was ebenfalls eine Fehlerquelle sein könnte (wenn sie fehlgeschlagen ist, ist ein Segmentierungsfehler sehr wahrscheinlich).

Sie sollten wirklich einige benannte Konstanten für 1.000 und 1.000.000 einführen; was repräsentieren sie?

Sie sollten auch eine Funktion schreiben, um die Berechnung durchzuführen; Sie könnten eine inline-Funktion in C99 oder höher (oder C++) verwenden. Die Wiederholung im Code ist unerträglich anzusehen.

Sie sollten auch C99-Notation für main() verwenden, mit dem expliziten Rückgabetyp (und vorzugsweise void für die Argumentliste, wenn Sie nicht argc oder argv verwenden):

int main(void) 

Aus reiner Neugier Ich habe eine Kopie Ihres Codes erstellt, alle Vorkommen von 1000 in ROWS geändert, alle Vorkommen von 1000000 in COLS und dann enum { ROWS = 1000, COLS = 10000 }; erstellt (wodurch die Größe des Problems um den Faktor 100 reduziert wurde).Ich habe ein paar kleinere Änderungen vorgenommen, so dass es unter meinen bevorzugten Kompilierungsoptionen sauber kompiliert werden würde (nichts Ernstes: static vor den Funktionen und das Haupt-Array; file wird zu einem lokalen main; Fehler überprüfen Sie die fopen(), etc.).

Ich erstellte dann eine zweite Kopie und erstellte eine Inline-Funktion, um die wiederholte Berechnung zu tun (und eine zweite, um tiefgestellte Berechnungen zu tun). Dies bedeutet, dass der monströse Ausdruck nur einmal ausgeschrieben wird - was sehr wünschenswert ist, um Konsistenz zu gewährleisten.

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

Dieses Programm schreibt P2.txt statt P1.txt. Ich habe beide Programme ausgeführt und die Ausgabedateien verglichen. die Ausgabe war identisch. Wenn ich die Programme auf einem meist ungenutzten Rechner (MacBook Pro, 2,3 GHz Intel Core i7, 16 GiB 1333 MHz RAM, Mac OS X 10.7.5, GCC 4.7.1) laufen ließ, bekam ich ein vernünftiges, aber nicht ganz konsistentes Timing:

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 

Die meiste Zeit wird jedoch für die Datenträger-E/A ausgegeben. Ich reduzierte die Disk-I/O nur die letzte Zeile von Daten, so dass die äußere I/O for Schleife wurde:

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

die Zeiten wurden erheblich reduziert und sehr viel konsequenter:

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 

Die Vereinfachung im Code von dem schrecklichen Ausdruck, der nur einmal ausgeschrieben wurde, ist sehr nützlich, scheint mir. Ich hätte dieses Programm sicherlich viel lieber beibehalten müssen als das Original.

Verwandte Themen