Связанный список не принимает следующий элемент (язык C) - PullRequest
0 голосов
/ 14 апреля 2019

Я новичок в языке C и работаю над примером связанного списка.

Функция initialize() работает нормально, но после первого вызова insert() программа вылетает.

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

Я работал над похожим примером со связанным списком, который имеет только элемент int, и он работал нормально.

Код выглядит следующим образом:

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

typedef struct Element Element;

struct Element
{
    char f_name[10];
    char l_name[10];
    float score;
    Element* next;
};

typedef struct Liste Liste;

struct Liste
{
    Element* first;
};

Element* read()
{
    Element* element = (Element*) malloc(sizeof(element));
    if(element==NULL)
        exit(EXIT_FAILURE);
    printf("Please provide first name : \n");
    scanf(" %s", element->f_name);
    printf("Please provide last name : \n");
    scanf(" %s", element->l_name);
    printf("Please provide score : \n");
    scanf("%f", &(element->score));
    return element;
}

Liste* initialize()
{
    Liste* liste = (Liste*) malloc(sizeof(liste));
    Element* element = (Element*) malloc(sizeof(element));
    if(liste==NULL || element==NULL)
        exit(EXIT_FAILURE);
    element = read();
    element->next = NULL;
    liste->first = element;
    return liste;
}

void insert(Liste* liste)
{
    Element* nouveau = (Element*) malloc(sizeof(nouveau));
    if(liste==NULL || nouveau==NULL)
        exit(EXIT_FAILURE);
    nouveau = read();
    nouveau->next = liste->first;
    liste->first = nouveau;
}

int main()
{
    Liste* maListe = (Liste*) malloc(sizeof(maListe));
    maListe = initialize();
    insert(maListe);
    insert(maListe);
    insert(maListe);
    return 0;
}

Что я сделал не так в этом? и как мне это исправить?

Спасибо.

Ответы [ 2 ]

3 голосов
/ 14 апреля 2019

Я думаю, что проблема в вашем случае в том, что вы написали sizeof(element), где вам нужно иметь sizeof(Element). У вас есть это в двух разных местах.

Обратите внимание, что "element" - это переменная типа указателя, поэтому она имеет размер указателя (вероятно, 8 байт), в то время как "Element" - это ваш тип структуры, который имеет гораздо больший размер. Таким образом, когда вы выделяете только sizeof(element) байтов, это слишком мало.

Подобные ошибки легко обнаруживаются при запуске вашей программы через valgrind.

1 голос
/ 14 апреля 2019

Хотя у вас уже есть ответ для вашего 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)

Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.

Посмотрите вещи и дайте мне знать, если у вас есть вопросы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...