2012-10-06 3 views
29

У меня проблема с какой-то программой, я искал ошибки сегментации, я не понимаю их достаточно хорошо, единственное, что я знаю, это то, что, по-видимому, я пытаюсь получить доступ к некоторой памяти. не должно. Проблема в том, что я вижу свой код и не понимаю, что я делаю неправильно.Ошибка сегментации: 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); 
} 

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

+0

В какой момент происходит segfault? –

+1

^Попробуйте использовать Valgrind, чтобы получить номер строки, где происходит segfault. Обычно это довольно очевидно, как только вы сузили его до одной строки - если нет, напишите, какая строка это, и мы можем помочь. –

+3

видя ваш код, во-первых, ваш компилятор, должно быть, прошел ошибку сегментации ... – perilbrain

ответ

62

Эта декларация:

double F[1000][1000000]; 

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

+0

спасибо, все. – Ariaramnes

1

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

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

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

Как таковой:

#define DIM1_SZ 1000 
#define DIM2_SZ 1000000 

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

0

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

29

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

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

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

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

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

Вы также должны использовать 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 

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

Смежные вопросы