У вас есть ряд проблем, которые необходимо исправить в вашем коде (некоторые из них больше, чем другие).
Ваша первая проблема может быть решена простым чтением Почему while (! Feof (file)) всегда неверно? .(краткий ответ: EOF
не генерируется в окончательном успешно , прочитанном fscanf
, поэтому после прочтения последнего "имени" вы проверяете !feof(file)
, который проверяет TRUE
, цикл продолжается,fscanf
завершается неудачно с EOF
, вы выделяете и затем пытаетесь strcpy
из buf
держать неопределенное значение - вызывая Неопределенное поведение )
В то время как в этом случае, гдечитая слова без пробелов, вы можете использовать fscanf
- но не используйте.Формируйте хорошие привычки рано.При получении входных данных из каждой строки файла используйте строчно-ориентированную функцию ввода (например, fgets
или POSIX getline
), чтобы убедиться, что то, что осталось во входном буфере, не зависит от scanf
используется спецификатор преобразования.
Например, вы можете:
#define MAXN 50 /* if you need a constant, #define one (or more) */
typedef struct node {
char name[MAXN];
struct node *next;
}
node;
... / * прочитать содержимое файла и записать в связанный список * / while (fgets (buf, MAXN, file)) {
/* valdiating the last char is '\n' or length < MAXN - 1 omitted */
/* allocate new node */
node *head = malloc (sizeof(node));
if (head == NULL) { /* validate allocation */
perror ("malloc-node"); /* issue error message */
return 1; /* don't return negative values to the shell */
}
/* trim '\n' from end of buf */
buf[strcspn(buf, "\n")] = 0; /* overwrite with nul-character */
/* initialize node values */
strcpy (head->name, buf);
head->next = NULL;
( note: не возвращает отрицательных значений в вашу оболочку и НЕ содержит пробел после оператора ->
при обращении к членам структуры)
На этом этапе вы успешно прочитали «имя» в buf
, выделенном для вашего нового узла, validated распределение выполнено успешно, обрезка завершающего '\n'
, включенного в buf
, с помощью линейно-ориентированной функции ввода, копирование обрезанного «имени» в head->name
и инициализация head->next
в NULL
.Однако у вас есть проблема.Все ваши имена хранятся в вашем списке в в обратном порядке (что иногда может быть тем, что вы хотите).Но это обычно не желаемое поведение.Просто объявив один дополнительный указатель, который обновляется так, чтобы он указывал на последний узел, вы можете выполнить вставку по порядку в список без итерации, чтобы найти последний узел.
Например, просто объявив last
указатель и проверка двух случаев (1) last=NULL
, указывающих, что цикл пуст, а указание first
и last
на узел first
позволяет (2) добавить все остальные узлы в конце, например:
node *first = NULL, *last = NULL; /* use last for in-order chaining */
...
/* initialize node values */
strcpy (head->name, buf);
head->next = NULL;
/* in-order add at end of list */
if (!last)
first = last = head;
else {
last->next = head;
last = head;
}
}
Ваш "Бесконечный цикл" был правильно адресован @ CS Pei , где вы просто забыли продвинуть текущий указатель на ptr->next
в вашем выходном цикле.
Вам дополнительно не удалось free
выделенной памяти.Да, он освобождается при выходе из программы, но имеет привычку отслеживать всю выделяемую память и затем освобождать эту память, когда она больше не нужна.(это становится жизненно важным навыком для предотвращения утечек памяти по мере роста вашего кода).Например, в вашем выходном цикле вы можете сделать:
node *ptr = first; /* declare/set pointer to first */
while (ptr != NULL) { /* loop while ptr */
node *victim = ptr; /* declare/set pointer to victim */
printf ("%s\n", ptr->name); /* output name */
ptr = ptr->next; /* advance pointer to next */
free (victim); /* free victim */
}
В целом, вы можете сделать что-то похожее на следующее:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 50 /* if you need a constant, #define one (or more) */
typedef struct node {
char name[MAXN];
struct node *next;
}
node;
int main (int argc, char **argv) {
FILE *file = argc > 1 ? fopen (argv[1], "r") : stdin;
if (file == NULL)
return 1;
char buf[MAXN];
node *first = NULL, *last = NULL; /* use last for in-order chaining */
/* read the contents of the file and write to linked list */
while (fgets (buf, MAXN, file)) {
/* valdiating the last char is '\n' or length < MAXN - 1 omitted */
/* allocate new node */
node *head = malloc (sizeof(node));
if (head == NULL) { /* validate allocation */
perror ("malloc-node"); /* issue error message */
return 1; /* don't return negative values to the shell */
}
/* trim '\n' from end of buf */
buf[strcspn(buf, "\n")] = 0; /* overwrite with nul-character */
/* initialize node values */
strcpy (head->name, buf);
head->next = NULL;
/* in-order add at end of list */
if (!last)
first = last = head;
else {
last->next = head;
last = head;
}
}
if (file != stdin) /* close file if not stdin */
fclose(file);
node *ptr = first; /* declare/set pointer to first */
while (ptr != NULL) { /* loop while ptr */
node *victim = ptr; /* declare/set pointer to victim */
printf ("%s\n", ptr->name); /* output name */
ptr = ptr->next; /* advance pointer to next */
free (victim); /* free victim */
}
return 0;
}
Пример использования / вывода
Используя ваш входной файл, вы получите следующий вывод:
$ ./bin/ll_chain_last <dat/llnames.txt
REDA
REDB
REDC
REDE
RED
REDb
REDc
REDpikachu
REDboom
Проверка использования памяти / ошибок
В любом кодеВы пишете, что динамически распределяет память, у вас есть 2 обязанностей относительно любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памяти, поэтому, (2) он может быть освобожден , когда он больше не нужен.
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за ее пределами.границы вашего выделенного блока, попытайтесь прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
- нормальный выбор.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через нее.
$ valgrind ./bin/ll_chain_last <dat/llnames.txt
==24202== Memcheck, a memory error detector
==24202== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24202== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==24202== Command: ./bin/ll_chain_last
==24202==
REDA
REDB
REDC
REDE
RED
REDb
REDc
REDpikachu
REDboom
==24202==
==24202== HEAP SUMMARY:
==24202== in use at exit: 0 bytes in 0 blocks
==24202== total heap usage: 9 allocs, 9 frees, 576 bytes allocated
==24202==
==24202== All heap blocks were freed -- no leaks are possible
==24202==
==24202== For counts of detected and suppressed errors, rerun with: -v
==24202== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.