私はいくつかのプログラムに問題があり、セグメンテーションフォールトについて検索しましたが、セグメンテーションフォールトを十分に理解していないため、私が知っている唯一のことは、おそらく私がすべきではないメモリにアクセスしようとしているということです。問題は、自分のコードを見て、私が間違っていることを理解していないことです。
#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);
}
御時間ありがとうございます。
この宣言:
double F[1000][1000000];
典型的なx86システムでは8 * 1000 * 1000000バイトを占有します。これは約7.45 GBです。コードを実行しようとしたときにシステムがメモリ不足になり、セグメンテーションエラーが発生する可能性があります。
アレイは約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
およびメイン配列; file
はmain
のローカルになります;エラーチェック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
恐ろしい表現を一度だけ書き出すことによるコードの簡素化は非常に有益であると私には思えます。確かに、オリジナルよりもそのプログラムを維持する必要があります。
どのシステムで実行していますか?何らかの種類のデバッガー(gdb、Visual Studioのデバッガーなど)にアクセスできますか?
これにより、プログラムがクラッシュするコード行などの貴重な情報が得られます。また、メモリ量が膨大になる可能性があります。
さらに、数値の制限を名前付き定義に置き換えることをお勧めしますか?
など:
#define DIM1_SZ 1000
#define DIM2_SZ 1000000
配列の次元の制限を参照したいときはいつでも使用してください。入力エラーを回避するのに役立ちます。