Почему я получаю ошибку сегментации? - PullRequest
3 голосов
/ 09 октября 2010

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

#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. Готово. Уф.

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

Ответы [ 8 ]

14 голосов
/ 09 октября 2010

Вы не выделили место для буфера.
Указатель 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.

РЕДАКТИРОВАТЬ:

Есть еще одна ошибка в:

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

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

Почему?

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

3 голосов
/ 09 октября 2010

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

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

2 голосов
/ 09 октября 2010

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

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

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

2 голосов
/ 09 октября 2010

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

1 голос
/ 09 октября 2010

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

  • Она не обрабатывает длинные строки правильно.Когда вы читаете буфер некоторого размера, может случиться так, что некоторое число начинается в конце буфера и продолжается в начале следующего буфера.Например, если у вас есть буфер размером 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 (сокращение от pointer ).

Вот мой код:

#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;
}
1 голос
/ 09 октября 2010
char *str;

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

char str[MAX_SIZE];
1 голос
/ 09 октября 2010

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

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

0 голосов
/ 09 октября 2010

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

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

...