63

私はいくつかのプログラムに問題があり、セグメンテーション違反について検索しました。それらをよく理解していないため、私が知っている唯一のことは、おそらく私がすべきではないメモリにアクセスしようとしているということです。問題は、自分のコードを見て、何が間違っているのか理解できないことです。

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

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

4

4 に答える 4

134

この宣言:

double F[1000][1000000];

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

于 2012-10-06T19:14:40.850 に答える
42

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

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

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

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

また、計算を行うための関数を作成する必要があります。inlineC99以降(またはC ++)の関数を使用できます。コードの繰り返しは、見るのが大変です。

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

int main(void)

アイドル状態の好奇心から、私はあなたのコードのコピーを取り、1000のすべてのオカレンスをROWSに、1000000のすべてのオカレンスをCOLSに変更してから作成しましたenum { ROWS = 1000, COLS = 10000 };(これにより、問題のサイズが100分の1に減少しました)。私はいくつかの小さな変更を加えて、私の好みのコンパイルオプションのセットの下できれいにコンパイルされるようにしました(深刻なことは何もありません:static関数とメイン配列の前;fileローカルになりmainます;エラーチェックfopen()など)。

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

#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/Oforループは次のようになりました。

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

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

于 2012-10-06T19:18:35.270 に答える
3

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

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

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

そのような:

#define DIM1_SZ 1000
#define DIM2_SZ 1000000

配列の次元制限を参照する場合は、これらを使用してください。入力ミスを防ぐのに役立ちます。

于 2012-10-06T19:12:27.307 に答える
1

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

于 2012-10-06T19:15:55.223 に答える