Что является причиной ошибки моего сегмента в C?

Когда я компилирую свой код, я не получаю ошибок. Однако, когда я пытаюсь запустить его, я получаю ошибку сегментации (дамп памяти). Вот мой главный:

Оригинальный код

void main(int argc, char *argv[]){
    if(argc < 3){
          return;
    }

    char *stop_list_name = argv[1];
    char *doc_names[argc - 2];

    int i;
    for(i = 0; i < argc; i++){
            doc_names[i] = argv[i];
    }

//create the array of stop words
    char *stopWords[50];
    char *word;
    int word_counter = 0;
    FILE *fp;
    fp = fopen(stop_list_name, "r");
    if(fp != NULL){
            while(!feof(fp)){
                    fscanf(fp, "%s", word);
                    stopWords[word_counter] = word;
                    word_counter++;
            }
    }

    fclose(fp);

    for(i = 0; stopWords[i] != '\0'; i++){
            printf("%s", stopWords[i]);
    }
}

Я уверен, что что-то не так в моем while цикл, но я точно не знаю, что или как это исправить.

Исправленный код

Увидев ответы, я изменил свой код так, чтобы он выглядел следующим образом, но все равно вылетал. Что сейчас не так?

int main(int argc, char *argv[]){
    if(argc < 3){
            return;
    }

    char *stop_list_name = argv[1];
    char *doc_names[argc - 2];

    int i;
    for(i = 2; i < argc; i++){
            doc_names[i-2] = argv[i];
    }

//create the array of stop words
    enum {MAX_STOP_WORDS = 50};
    char *stopWords[MAX_STOP_WORDS];
    int word_counter = 0;
    FILE *fp = fopen(stop_list_name, "r");
    if(fp != NULL){
            char word[64];
            int i;
            for(i = 0; i < MAX_STOP_WORDS && fscanf(fp, "%63s", word) == 1; i++){
                    stopWords[i] = strdup(word);
            }

            word_counter = i;
            fclose(fp);
    }

    for(i = 0; stopWords[i] != '\0'; i++){
            printf("%s", stopWords[i]);
    }
}

2 ответа

Решение

Проблемы в оригинальном коде

Один из возможных источников проблем:

char *doc_names[argc - 2];

int i;
for(i = 0; i < argc; i++){
        doc_names[i] = argv[i];
}

Вы выделяете место для argc-2 указатели и приступить к копированию argc указатели в это пространство. Это переполнение буфера (в данном случае переполнение стека тоже). Это может легко вызвать проблемы. Возможное исправление:

for (i = 2; i < argv; i++)
    doc_names[i-2] = argv[i];

Однако вам действительно не нужно копировать список аргументов; Вы можете просто обработать аргументы от индекса 2 до конца. Я отмечаю, что показанный код на самом деле не использует doc_names, но назначение за пределами поля все еще может вызвать проблемы.


Вы не выделяете пространство для чтения слова и не выделяете новое пространство для каждого стоп-слова, а также не гарантируете, что не переполняете границы массива, в котором храните слова.

Рассмотрите возможность использования:

enum { MAX_STOP_WORDS = 50 };
char *stopWords[MAX_STOP_WORDS];
int word_counter = 0;
FILE *fp = fopen(stop_list_name, "r");
if (fp != NULL)
{
    char word[64];
    for (i = 0; i < MAX_STOP_WORDS && fscanf(fp, "%63s", word) == 1; i++)
        stopWords[i] = strdup(word);
    word_counter = i;
    fclose(fp);
}

Эта диагностированная проблема определенно является вероятной причиной вашей аварии. я использовал i (объявлено ранее в коде) в цикле, потому что word_counter делает линию управления циклом слишком длинной для SO.

Строго, strdup() не является частью стандарта C, но является частью POSIX. Если у вас нет POSIX, вы можете написать свой собственный:

#include <stdlib.h>
#include <string.h>

char *strdup(const char *str)
{
    size_t len = strlen(str) + 1;
    char *result = malloc(len);
    if (result != 0)
        memmove(result, str, len);
    return result;
}

У вас также есть некоторые другие плохие практики на дисплее:


Проблемы в исправленном коде

В исправленном коде есть одна важная и пара очень незначительных проблем:

  • Ваш цикл, который печатает стоп-слова, зависит от нулевого указателя (как ни странно '\0' - это допустимое, но нетрадиционное написание для нулевого указателя), но код инициализации не устанавливает нулевой указатель.

    Есть (по крайней мере) два варианта исправления:

    1. Добавить нулевой указатель:

         for (i = 0; i < MAX_STOP_WORDS-1 && fscanf(fp, "%63s", word) == 1; i++)
             stopWords[i] = strdup(word);
      
         stopWords[i] = 0;
         fclose(fp);
      }
      
      for (i = 0; stopWords[i] != '\0'; i++)
          printf("%s\n", stopWords[i]);
      

      Обратите внимание, что верхняя граница теперь MAX_STOP_WORDS - 1,

    2. Или вы можете использовать wordCount вместо условия:

      for (i = 0; i < wordCount; i++)
          printf("%s\n", stopWords[i]);
      

    Я бы выбрал второй вариант.

  • Одна из причин этого заключается в том, что он избегает предупреждений о wordCount быть установленным и не использованным - небольшая проблема.

  • А также doc_names также установлен, но не используется.

Я беспокоюсь о них, потому что мои параметры компилятора по умолчанию генерируют ошибки для неиспользуемых переменных - поэтому код не компилируется, пока я не исправлю это. Это приводит к:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        fprintf(stderr, "Usage: %s stop-words docfile ...\n", argv[0]);
        return 1;
    }

    char *stop_list_name = argv[1];
    char *doc_names[argc - 2];

    int i;
    for (i = 2; i < argc; i++)
    {
        doc_names[i - 2] = argv[i];
    }
    int doc_count = argc - 2;

    // create the array of stop words
    enum { MAX_STOP_WORDS = 50 };
    char *stopWords[MAX_STOP_WORDS];
    int word_counter = 0;
    FILE *fp = fopen(stop_list_name, "r");
    if (fp != NULL)
    {
        char word[64];
        int i;
        for (i = 0; i < MAX_STOP_WORDS && fscanf(fp, "%63s", word) == 1; i++)
            stopWords[i] = strdup(word);

        word_counter = i;
        fclose(fp);
    }

    for (i = 0; i < word_counter; i++)
        printf("stop word %d: %s\n", i, stopWords[i]);

    for (i = 0; i < doc_count; i++)
        printf("document %d: %s\n", i, doc_names[i]);

    return 0;
}

И, учитывая файл стоп-слов, содержащий:

help
able
may
can
it
should
do
antonym
prozac

и компилируя его (исходный файл sw19.c, программа sw19) с:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
>     -Wold-style-definition -Werror sw19.c -o sw19

и запустить его как:

$ ./sw19 stopwords /dev/null
stop word 0: help
stop word 1: able
stop word 2: may
stop word 3: can
stop word 4: it
stop word 5: should
stop word 6: do
stop word 7: antonym
stop word 8: prozac
document 0: /dev/null
$

Вы пытаетесь сохранить отсканированную строку в неинициализированном указателе,

fscanf(fp, "%s", word);

а также word, даже не инициализируется.

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

char word[100];

if (fscanf(fp, "%99s", word) != 1)
    word[0] = '\0'; /* ensure that `word' is nul terminated on input error */

Также, while (!feof(fp)) неправильно, потому что EOF маркер не будет установлен до fscanf() пытается прочитать после конца файла, поэтому код будет повторяться еще один раз. И в этом случае вы бы хранить то же самое word дважды.

Обратите внимание, что вам также нужно выделить место для массива указателей, возможно, там вы могли бы использовать malloc(),

Другие вопросы по тегам