Подтвердить что ты не робот

Ошибка сегментации: 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);
}

Спасибо за ваше время.

4b9b3361

Ответ 1

Это объявление:

double F[1000][1000000];

будет занимать 8 * 1000 * 1000000 байт в типичной системе x86. Это около 7,45 ГБ. Скорее всего, ваша система исчерпала память при попытке выполнить ваш код, что приводит к ошибке сегментации.

Ответ 2

Ваш массив занимает примерно 8 ГБ памяти (1 000 x 1 000 000 x sizeof (double) байтов). Это может быть фактором в вашей проблеме. Это глобальная переменная, а не переменная стека, поэтому вы можете быть в порядке, но здесь вы нажимаете ограничения.

Запись большого количества данных в файл займет некоторое время.

Вы не проверяете, что файл был успешно открыт, что также может стать источником проблем (если он сработал, вероятность сегментации очень вероятна).

Вам действительно нужно ввести некоторые именованные константы для 1000 и 1 000 000; что они представляют?

Вы также должны написать функцию для вычисления; вы можете использовать функцию inline в C99 или более поздней версии (или С++). Повторение в коде мучительно, чтобы созерцать.

Вы также должны использовать нотацию C99 для main(), с явным типом возвращаемого значения (и предпочтительно void для списка аргументов, когда вы не используете argc или argv):

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 ГГц Intel Core i7, 16 гигабайт 1333 МГц, 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

Однако почти все это время тратится на дисковый ввод-вывод. Я сократил дисковый ввод-вывод до самой последней строки данных, поэтому цикл внешнего ввода-вывода 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

Мне кажется, что упрощение кода из-за того, что у меня есть ужасное выражение, выписанное один раз, очень полезно. Я, конечно, гораздо лучше должен поддерживать эту программу, чем оригинал.

Ответ 3

В какой системе вы работаете? У вас есть доступ к каким-то отладчикам (gdb, отладчик визуальной студии и т.д.)?

Это даст нам ценную информацию, такую ​​как строка кода, в которой программа выйдет из строя... Кроме того, объем памяти может быть непомерно высоким.

Кроме того, могу ли я рекомендовать заменить числовые ограничения указанными определениями?

Таким образом:

#define DIM1_SZ 1000
#define DIM2_SZ 1000000

Используйте их, когда хотите обратиться к ограничениям размера массива. Это поможет избежать ошибок ввода.

Ответ 4

Запустите свою программу с помощью valgrind связанного с efence. Это скажет вам, где указатель разыменован и, скорее всего, устранит вашу проблему, если вы исправите все ошибки, о которых они рассказывают.