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.45 GB。尝试执行代码时,您的系统可能内存不足,从而导致分段错误。

于 2012-10-06T19:14:40.850 回答
42

您的数组占用大约 8 GB 内存(1,000 x 1,000,000 x sizeof(double) 字节)。这可能是您的问题的一个因素。它是一个全局变量而不是堆栈变量,所以你可能没问题,但你在这里推动了限制。

将这么多数据写入文件需要一段时间。

您没有检查文件是否已成功打开,这也可能是麻烦的根源(如果确实失败,则很可能是分段错误)。

你真的应该为 1,000 和 1,000,000 引入一些命名常量;它们代表什么?

您还应该编写一个函数来进行计算;您可以使用inlineC99 或更高版本(或 C++)中的函数。代码中的重复令人痛苦。

您还应该使用 C99 表示法main(),使用显式返回类型(最好在不使用orvoid时用于参数列表):argcargv

int main(void)

出于好奇,我复制了您的代码,将所有出现的 1000 更改为 ROWS,将所有出现的 1000000 更改为 COLS,然后创建enum { ROWS = 1000, COLS = 10000 };(从而将问题大小减少了 100 倍)。我做了一些小的改动,所以它可以在我喜欢的编译选项集下干净地编译(没什么严重的:static在函数前面,和主数组;file成为本地main;错误检查fopen()等)。

然后我创建了第二个副本并创建了一个内联函数来进行重复计算(以及第二个进行下标计算)。这意味着可怕的表达式只被写出一次——这是非常可取的,因为它确保了一致性。

#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 回答