链接列表不接受下一个元素(C语言)

问题描述 投票:0回答:2

我是C语言的新手,我正在开发一个Linked List示例。

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;
}

我在这个方面做错了什么?我该如何解决?

谢谢。

c linked-list
2个回答
3
投票

我认为你的问题是你写了sizeof(element),你需要有sizeof(Element)。你有两个不同的地方。

请注意,“element”是指针类型的变量,因此它具有指针的大小(可能是8个字节),而“Element”是具有更大尺寸的struct类型。所以,当你只分配太小的sizeof(element)字节时。

通过valgrind运行程序很容易找到这种错误。


1
投票

虽然您已经有了SegFault的答案,但还有一些方面可以清理和重构代码,以便更有效地协同工作。由于您使用列表结构liste来保存指向first中列表开头的指针,因此您还可以添加另一个指针last以指向列表中的最后一个节点,并且无需在每次插入时迭代到最后一个节点。使用last(或tail)指针,您的新节点始终插入last->next。例如,你Liste结构可能是:

typedef struct Liste Liste;

struct Liste {
    Element *first, *last;
};

你的列表函数应该分别做一件事,这意味着initialize()应该只分配和初始化Liste节点及其指针。 read()应该分配并读取并返回一个指向已填充节点的有效指针,或者在失败时返回NULL。 insert()应该这样做,从Liste获取read()列表addressm和节点并将其插入列表中。将这些功能放在一起你可以做到:

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;
    }
}

(注意:初始化和插入是直接的,你只处理的两个类是你是插入第一个节点,还是所有其他节点。使它非常简单)

完全放在一起,您可以编写完整的测试代码,如下所示添加一个函数来迭代列表打印值,然后一个类似的函数迭代列表释放节点,然后最后列表::

#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)当它为no时可以释放它需要更久。

您必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认你释放了你分配的所有内存。

对于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)

始终确认已释放已分配的所有内存并且没有内存错误。

仔细看看,如果您有疑问,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.