web-dev-qa-db-fra.com

défaut de segmentation: 11

J'ai un problème avec un programme, j'ai cherché des erreurs de segmentation, mais je ne les comprends pas très bien. La seule chose que je sache, c'est que j'essaie probablement d'accéder à une mémoire que je ne devrais pas. Le problème est que je vois mon code et ne comprends pas ce que je fais mal.

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

Merci pour votre temps.

45
Ariaramnes

Cette déclaration:

double F[1000][1000000];

occuperait 8 * 1000 * 1000000 octets sur un système x86 typique. C'est environ 7,45 Go. Il est probable que votre système manque de mémoire lorsque vous essayez d'exécuter votre code, ce qui entraîne une erreur de segmentation.

88
unwind

Votre matrice occupe environ 8 Go de mémoire (1 000 x 1 000 000 x taillede (double) octets). Cela pourrait être un facteur dans votre problème. Il s’agit d’une variable globale plutôt que d’une variable de pile; vous êtes donc OK, mais vous repoussez les limites ici.

Écrire autant de données dans un fichier va prendre un certain temps.

Vous ne vérifiez pas que le fichier a été ouvert avec succès, ce qui pourrait également être une source de problèmes (s'il échouait, une erreur de segmentation serait très probable).

Vous devriez vraiment introduire des constantes nommées pour 1 000 et 1 000 000; que représentent-ils?

Vous devriez également écrire une fonction pour faire le calcul; vous pouvez utiliser une fonction inline dans C99 ou une version ultérieure (ou C++). La répétition dans le code est insoutenable à voir.

Vous devez également utiliser la notation C99 pour main(), avec le type de retour explicite (et de préférence void pour la liste d'arguments lorsque vous n'utilisez pas argc ou argv):

int main(void)

Par curiosité oisive, j'ai pris une copie de votre code, changé toutes les occurrences de 1000 en ROWS, toutes les occurrences de 1000000 en COLS, puis créé enum { ROWS = 1000, COLS = 10000 }; (réduisant ainsi la taille du problème d'un facteur 100). J'ai apporté quelques modifications mineures afin que la compilation se fasse proprement sous mon ensemble d'options de compilation préféré (rien de grave: static devant les fonctions, et le tableau principal; file devient un local à main; erreur vérifier la fopen(), etc.).

J'ai ensuite créé une deuxième copie et créé une fonction en ligne pour effectuer le calcul répété (et une seconde pour effectuer des calculs en indice). Cela signifie que l'expression monstrueuse n'est écrite qu'une seule fois - ce qui est hautement souhaitable, car elle garantit la cohérence.

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

Ce programme écrit sur P2.txt au lieu de P1.txt. J'ai exécuté les deux programmes et comparé les fichiers de sortie; la sortie était identique. Lorsque j’exécutais les programmes sur un ordinateur quasiment inactif (MacBook Pro, Intel Core i7 à 2,3 GHz, 16 GiB 1333 MHz, Mac OS X 10.7.5, GCC 4.7.1), j’ai eu raison, mais pas timing totalement cohérent:

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

Cependant, presque tout ce temps est consacré aux E/S du disque. J'ai réduit les E/S du disque à la toute dernière ligne de données. La boucle externe E/S for est ainsi devenue:

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

les timings ont été considérablement réduits et beaucoup plus cohérents:

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

Il me semble que la simplification du code consistant à écrire l'expression horrible une seule fois est très bénéfique. Je préférerais certainement de loin maintenir ce programme plutôt que l'original.

34
Jonathan Leffler

Sur quel système utilisez-vous? Avez-vous accès à une sorte de débogueur (gdb, le débogueur de Visual Studio, etc.)?

Cela nous donnerait des informations précieuses, telles que la ligne de code où le programme se bloque ... De plus, la quantité de mémoire peut être prohibitive.

De plus, puis-je vous recommander de remplacer les limites numériques par des définitions nommées?

En tant que tel:

#define DIM1_SZ 1000
#define DIM2_SZ 1000000

Utilisez-les chaque fois que vous souhaitez faire référence aux limites de dimension du tableau. Cela aidera à éviter les erreurs de frappe.

1
Blitzkoder

Exécutez votre programme avec valgrind of lié à efence . Cela vous indiquera où le pointeur est en train d'être déréférencé et corrigera probablement votre problème si vous corrigez toutes les erreurs évoquées.

0
Gung Foo