2010-10-09 11 views
3

Я пытаюсь написать программу, которая принимает файл открытого текста в качестве аргумента и анализирует его, добавляя все числа вместе, а затем распечатывая сумму. Ниже мой код:Почему я получаю ошибку сегментации?

#include <stdio.h> 
#include <stdlib.h> 
#include <ctype.h> 

static int sumNumbers(char filename[]) 
{ 
    int sum = 0; 
    FILE *file = fopen(filename, "r"); 
    char *str; 

    while (fgets(str, sizeof BUFSIZ, file)) 
    { 
     while (*str != '\0') 
     { 
      if (isdigit(*str)) 
      { 
       sum += atoi(str); 
       str++; 
       while (isdigit(*str)) 
        str++; 
       continue; 
      } 
      str++; 
     } 
    } 

    fclose(file); 

    return sum; 
} 

int main(int argc, char *argv[]) 
{ 
    if (argc != 2) 
    { 
     fprintf(stderr, "Please enter the filename as the argument.\n"); 
     exit(EXIT_FAILURE); 
    } 
    else 
    { 
     printf("The sum of all the numbers in the file is : %d\n", sumNumbers(argv[1])); 
     exit(EXIT_SUCCESS); 
    } 

    return 0; 
} 

И текстовый файл я использую:

Это довольно скучный текстовый файл с некоторые случайные числа разбросаны на всем ее протяжении.

Вот один: 87 и вот еще: 3

и, наконец, две последние цифры: 12 19381. Готово. Уф.

Когда я компилирую и пытаюсь запустить его, у меня возникает ошибка сегментации.

ответ

14

Вы не выделили место для буфера.
Указатель str - это всего лишь висячий указатель. Таким образом, ваша программа эффективно сбрасывает данные, считанные из файла, в место памяти, которое у вас нет, что приводит к ошибке сегментации.

Понадобится:

char *str; 
str = malloc(BUFSIZ); // this is missing..also free() the mem once done using it. 

или просто:

char str[BUFSIZ]; // but then you can't do str++, you'll have to use another 
        // pointer say char *ptr = str; and use it in place of str. 

EDIT:

Существует еще одна ошибка в:

while (fgets(str, sizeof BUFSIZ, file)) 

Второй аргумент должен быть BUFSIZ не sizeof BUFSIZ.

Почему?

Потому что второй аргумент - это максимальное количество символов, которые нужно прочитать в буфере, включая нулевой символ. Так как sizeof BUFSIZ - 4, вы можете прочитать max до 3 char в буфер. Это причина, по которой 19381 был прочитан как 193, а затем 81<space>.

+0

Эй спасибо он работает сейчас. Но если вы не возражаете, несите меня еще ненадолго. Если вы попытаетесь запустить программу после ее исправления, по какой-то причине 'atoi()' анализирует число '19381' как' 193' и '81' соответственно. Любая идея, почему это происходит? – jon2512chua

+0

@Jon: Обновлен ответ :) – codaddict

+0

Owh Я вижу это сейчас, спасибо! :) – jon2512chua

2

Потому что вы не выделили место для своего буфера.

3

Вы не указали какую-либо ячейку памяти для заполнения str. fgets принимает в качестве первого аргумента буфер, а не неназначенный указатель.

Вместо char *str; вам нужно определить достаточно размера буфера, скажем, char str[BUFSIZ];

1

Вы объявили символ * ул, но вы не отложите память для него просто нет. Для этого вам понадобится память malloc.

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

1
char *str; 

У str нет выделенной памяти для него. Либо используйте malloc(), чтобы выделить для него некоторую память, либо объявили ее с предопределенным размером.

char str[MAX_SIZE]; 
2

Некоторые люди уже рассмотрели проблему, о которой вы просили, но у меня есть вопрос взамен. Что же вы думаете, это выполняет:

 if (isdigit(*str)) 
     { 
      if (isdigit(*str)) 
      { 
       sum += atoi(str); 
       str++; 
       while (isdigit(*str)) 
        str++; 
       continue; 
      } 
     } 

Что должно быть точкой двух последовательных if заявлений с точно таким же состоянием? (Примечание для записи: ни у кого нет предложения else).

+0

славный улов ... + 1 для просмотра за пределами очевидного =) –

+0

К сожалению, это была опечатка. Должно быть, слишком устал. – jon2512chua

1

Ваша программа имеет несколько ошибок:

  • Он не обрабатывает длинные строки корректно. Когда вы читаете буфер определенного размера, может случиться, что некоторое число начинается в конце буфера и продолжается в начале следующего буфера. Например, если у вас есть буфер размером 4, может быть вход The |numb|er 1|2345| is |larg|e., где вертикальные линии указывают содержимое буфера. Затем вы посчитали бы 1 и 2345 отдельно.
  • В качестве аргумента он вызывает isdigit с аргументом char. Как только вы прочтете какой-либо «большой» символ (больше SCHAR_MAX), поведение не определено. Ваша программа может вылететь из строя или произвести неверные результаты или сделать то, что она хочет сделать. Чтобы исправить это, вы должны сначала присвоить значение unsigned char, например isdigit((unsigned char) *str). Или, как и в моем коде, вы можете подать ему значение из функции fgetc, которая гарантированно будет действительным аргументом для isdigit.
  • Вы используете функцию, которая требует буфера (fgets), но вы не можете выделить буфер. Как отмечали другие, самый простой способ получить буфер - объявить локальную переменную char buffer[BUFSIZ].
  • Вы используете переменную str для двух целей: удерживать адрес буфера (который должен оставаться постоянным в течение всего времени выполнения) и указатель для анализа текста (который изменяется во время выполнения). Сделайте эти две переменные. Я бы назвал их buffer и p (сокращение от указатель).

Вот мой код:

#include <ctype.h> 
#include <stdio.h> 

static int sumNumbers(const char *filename) 
{ 
    int sum, num, c; 
    FILE *f; 

    if ((f = fopen(filename, "r")) == NULL) { 
     /* TODO: insert error handling here. */ 
    } 

    sum = 0; 
    num = 0; 
    while ((c = fgetc(f)) != EOF) { 
     if (isdigit(c)) { 
      num = 10 * num + (c - '0'); 
     } else if (num != 0) { 
      sum += num; 
      num = 0; 
     } 
    } 

    if (fclose(f) != 0) { 
     /* TODO: insert error handling here. */ 
    } 

    return sum; 
} 

int main(int argc, char **argv) { 
    int i; 

    for (i = 1; i < argc; i++) 
     printf("%d\t%s\n", sumNumbers(argv[i]), argv[i]); 
    return 0; 
} 
+0

Спасибо за отличную обратную связь! Ценить это! :) – jon2512chua

0

Вот функция, которая делает свою работу:

static int sumNumbers(char* filename) { 
    int sum = 0; 
    FILE *file = fopen(filename, "r"); 
    char buf[BUFSIZ], *str; 

    while (fgets(buf, BUFSIZ, file)) 
    { 
      str=buf; 
      while (*str) 
      { 
        if (isdigit(*str)) 
        { 
          sum += strtol(str, &str, 10); 
        } 
        str++; 
      } 
    } 
    fclose(file); 
    return sum; 
} 

Это не включает обработку ошибок, но работает достаточно хорошо. Для вашего файла, выход будет

сумма всех чисел в файле: 19483