web-dev-qa-db-ja.com

セグメンテーション障害: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);
}

御時間ありがとうございます。

45
Ariaramnes

この宣言:

double F[1000][1000000];

典型的なx86システムでは8 * 1000 * 1000000バイトを占有します。これは約7.45 GBです。コードを実行しようとしたときにシステムがメモリ不足になり、セグメンテーションエラーが発生する可能性があります。

88
unwind

アレイは約8 GBのメモリ(1,000 x 1,000,000 x sizeof(double)バイト)を占有しています。それが問題の要因かもしれません。これはスタック変数ではなくグローバル変数なので、大丈夫かもしれませんが、ここで制限を課しています。

その量のデータをファイルに書き込むには時間がかかります。

ファイルが正常に開かれたことを確認しないので、これもトラブルの原因になる可能性があります(失敗した場合は、セグメンテーション違反が発生する可能性が非常に高くなります)。

1,000と1,000,000の名前付き定数を実際に導入する必要があります。彼らは何を表していますか?

計算を行う関数も作成する必要があります。 C99以降(またはC++)でinline関数を使用できます。コード内の繰り返しは目を見張るものがあります。

main()にもC99表記を使用し、明示的な戻り値の型を使用する必要があります(voidまたはargcを使用していない場合は、引数リストにargvを使用することをお勧めします)。

int main(void)

アイドル状態の好奇心から、コードのコピーを取り、1000のすべての出現をROWSに、1000000のすべての出現をCOLSに変更し、enum { ROWS = 1000, COLS = 10000 };を作成しました(これにより、問題のサイズを100分の1に削減しました)。いくつかの小さな変更を加えて、好みのコンパイルオプションセットの下できれいにコンパイルできるようにしました(関数の前にあるstaticおよびメイン配列; filemainのローカルになります;エラーチェックfopen()、等。)。

次に、2番目のコピーを作成し、繰り返し計算を行うためのインライン関数を作成しました(2番目の関数は下付き文字の計算を行います)。これは、巨大な式が一度だけ書き出されることを意味します。一貫性を確保するため、これは非常に望ましいことです。

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

このプログラムは、P2.txtの代わりにP1.txtに書き込みます。両方のプログラムを実行し、出力ファイルを比較しました。出力は同じでした。ほとんどアイドル状態のマシン(MacBook Pro、2.3 GHz Intel Core i7、16 GiB 1333 MHz RAM、Mac OS X 10.7.5、GCC 4.7.1)でプログラムを実行すると、合理的には取得できましたが、完全に一貫したタイミング:

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

ただし、そのほとんどの時間はディスクI/Oに費やされます。ディスクI/Oをデータの最後の行だけに減らしたため、外側のI/O forループは次のようになりました。

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

タイミングが大幅に短縮され、非常に一貫性が増しました。

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

恐ろしい表現を一度だけ書き出すことによるコードの簡素化は非常に有益であると私には思えます。確かに、オリジナルよりもそのプログラムを維持する必要があります。

34

どのシステムで実行していますか?何らかの種類のデバッガー(gdb、Visual Studioのデバッガーなど)にアクセスできますか?

これにより、プログラムがクラッシュするコード行などの貴重な情報が得られます。また、メモリ量が膨大になる可能性があります。

さらに、数値の制限を名前付き定義に置き換えることをお勧めしますか?

など:

#define DIM1_SZ 1000
#define DIM2_SZ 1000000

配列の次元の制限を参照したいときはいつでも使用してください。入力エラーを回避するのに役立ちます。

1
Blitzkoder

valgrind にリンクされた efence でプログラムを実行します。これにより、ポインターが逆参照されている場所がわかり、それらが示すすべてのエラーを修正した場合に問題が解決する可能性が高くなります。

0
Gung Foo