Хотя у вас уже есть ответ для вашего SegFault, существуют дополнительные области, в которых вы можете очистить и реорганизовать свой код для более эффективной совместной работы. Поскольку вы используете структуру списка liste
для хранения указателя на начало списка в first
, вы также можете добавить еще один указатель last
, чтобы указать на последний узел в списке, и избавиться от необходимости повторяться в последний узел на каждой вставке. С указателем last
(или tail
) ваш новый узел всегда вставляется в last->next
. Например, вы Liste
struct могли бы быть:
typedef struct Liste Liste;
struct Liste {
Element *first, *last;
};
Ваши функции списка должны выполнять одно, то есть initialize()
должен просто выделять и инициализировать узел Liste
и его указатели. read()
должен выделять, читать и возвращать действительный указатель на заполненный узел или NULL в случае сбоя. insert()
должен сделать именно это, взять адресный список Liste
и узел из read()
и вставить его в список. Соединяя эти функции вместе, вы можете сделать:
Element *read()
{
Element *element = malloc (sizeof(*element)); /* allocate */
if (element == NULL) /* validate */
return NULL;
element->next = NULL; /* initialize */
printf ("\nPlease provide first name : ");
if (scanf ("%9s", element->f_name) != 1) /* validate EVERY input */
goto badread;
printf ("Please provide last name : ");
if (scanf ("%9s", element->l_name) != 1)
goto badread;
printf ("Please provide score : ");
if (scanf ("%f", &element->score) != 1)
goto badread;
return element; /* return allocated and initialized element */
badread:; /* just a simple goto label for handling read error */
free (element); /* free memory of node if error */
return NULL;
}
( примечание: использование goto
для отправки вас на метку, выходящую за рамки нормального возврата, где вы можете освободить память для узла, который потерпел неудачу во время заполнения.)
/* initialize the list, don't worry about the elements */
Liste *initialize (void)
{
Liste *liste = malloc(sizeof *liste);
if (liste == NULL) {
perror ("malloc-liste"); /* give some meaningful error */
exit (EXIT_FAILURE);
}
liste->first = liste->last = NULL;
return liste;
}
void insert (Liste *liste, Element *nouveau)
{
if (liste == NULL || nouveau == NULL)
exit (EXIT_FAILURE);
if (!liste->first) /* inserting 1st node */
liste->first = liste->last = nouveau;
else { /* inserting all others */
liste->last->next = nouveau;
liste->last = nouveau;
}
}
( примечание: инициализация и вставка прямолинейны, только два класса, с которыми вы обращаетесь, это то, вставляете ли вы 1-й узел или все другие узлы. Делает это очень простым)
В целом, вы можете написать свой полный тестовый код следующим образом, добавив функцию для итерации по значениям печати списка, а затем аналогичную функцию для итерации по узлам освобождения списка и, наконец, по списку ::
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 10 /* if you need a constant, #define one (or more) */
typedef struct Element Element;
struct Element {
char f_name[MAXN];
char l_name[MAXN];
float score;
Element* next;
};
typedef struct Liste Liste;
struct Liste {
Element *first, *last;
};
Element *read()
{
Element *element = malloc (sizeof(*element)); /* allocate */
if (element == NULL) /* validate */
return NULL;
element->next = NULL; /* initialize */
printf ("\nPlease provide first name : ");
if (scanf ("%9s", element->f_name) != 1) /* validate EVERY input */
goto badread;
printf ("Please provide last name : ");
if (scanf ("%9s", element->l_name) != 1)
goto badread;
printf ("Please provide score : ");
if (scanf ("%f", &element->score) != 1)
goto badread;
return element; /* return allocated and initialized element */
badread:; /* just a simple goto label for handling read error */
free (element); /* free memory of node if error */
return NULL;
}
/* initialize the list, don't worry about the elements */
Liste *initialize (void)
{
Liste *liste = malloc(sizeof *liste);
if (liste == NULL) {
perror ("malloc-liste"); /* give some meaningful error */
exit (EXIT_FAILURE);
}
liste->first = liste->last = NULL;
return liste;
}
void insert (Liste *liste, Element *nouveau)
{
if (liste == NULL || nouveau == NULL)
exit (EXIT_FAILURE);
if (!liste->first) /* inserting 1st node */
liste->first = liste->last = nouveau;
else { /* inserting all others */
liste->last->next = nouveau;
liste->last = nouveau;
}
}
void prnlist (Liste *liste)
{
Element *iter = liste->first;
while (iter) { /* just iterate list outputting values */
printf ("%-10s %-10s -> %.2f\n",
iter->f_name, iter->l_name, iter->score);
iter = iter->next;
}
}
void freelist (Liste *liste)
{
Element *iter = liste->first;
while (iter) {
Element *victim = iter;
iter = iter->next; /* iterate to next node BEFORE */
free (victim); /* you free victim */
}
free (liste);
}
int main (void) {
Liste *maListe = initialize(); /* create/initialize list */
Element *node;
while ((node = read())) /* allocate/read */
insert (maListe, node); /* insert */
puts ("\n\nElements in list:\n"); /* output list values */
prnlist (maListe);
freelist (maListe); /* don't forget to free what you allocate */
return 0;
}
Пример использования / Вывод
$ ./bin/ll_liste
Please provide first name : Donald
Please provide last name : Duck
Please provide score : 99.2
Please provide first name : Minnie
Please provide last name : Mouse
Please provide score : 99.7
Please provide first name : Pluto
Please provide last name : Dog
Please provide score : 83.5
Please provide first name :
Elements in list:
Donald Duck -> 99.20
Minnie Mouse -> 99.70
Pluto Dog -> 83.50
Использование памяти / проверка ошибок
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанности в отношении любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памяти, таким образом, (2) он может быть освобожден , когда он больше не нужен.
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы гарантировать, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или основать условный переход на неинициализированном значении и, наконец, , чтобы подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
- нормальный выбор. Для каждой платформы есть похожие проверки памяти. Все они просты в использовании, просто запустите вашу программу через него.
$ valgrind ./bin/ll_liste
==10838== Memcheck, a memory error detector
==10838== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10838== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==10838== Command: ./bin/ll_liste
==10838==
Please provide first name : Donald
Please provide last name : Duck
Please provide score : 99.2
Please provide first name : Minnie
Please provide last name : Mouse
Please provide score : 99.6
Please provide first name : Pluto
Please provide last name : Dog
Please provide score : 87.2
Please provide first name :
Elements in list:
Donald Duck -> 99.20
Minnie Mouse -> 99.60
Pluto Dog -> 87.20
==10838==
==10838== HEAP SUMMARY:
==10838== in use at exit: 0 bytes in 0 blocks
==10838== total heap usage: 5 allocs, 5 frees, 144 bytes allocated
==10838==
==10838== All heap blocks were freed -- no leaks are possible
==10838==
==10838== For counts of detected and suppressed errors, rerun with: -v
==10838== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Посмотрите вещи и дайте мне знать, если у вас есть вопросы.