Что является причиной ошибки моего сегмента в 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;
}
У вас также есть некоторые другие плохие практики на дисплее:
while (!feof(file))
всегда неправильно.- Что должно
main()
вернуть в C и C++? - Вы должны только позвонить
fclose(fp)
еслиfopen()
работал, так что вам нужно переместитьfclose()
внутриif
тело заявления.
Проблемы в исправленном коде
В исправленном коде есть одна важная и пара очень незначительных проблем:
Ваш цикл, который печатает стоп-слова, зависит от нулевого указателя (как ни странно
'\0'
- это допустимое, но нетрадиционное написание для нулевого указателя), но код инициализации не устанавливает нулевой указатель.Есть (по крайней мере) два варианта исправления:
Добавить нулевой указатель:
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
,Или вы можете использовать
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()
,