我是C语言的新手,我正在研究链接列表示例。
well_column
函数似乎可以正常工作,但是在第一次调用initialize()
之后,程序崩溃了。
我认为问题出在将新元素添加到链表中时,好像它溢出或不接受列表中的第一个新元素。
我处理了一个类似的示例,其中的链接列表只有一个insert()
元素,并且工作正常。
代码如下:
int
这件事我做错了什么?以及我该如何解决?
谢谢。
答案 0 :(得分:3)
我认为您遇到的问题是您在需要sizeof(element)
的地方写了sizeof(Element)
。您在两个不同的地方都有它。
请注意,“ element”是指针类型的变量,因此它具有指针的大小(可能为8个字节),而“ Element”是您的结构类型,其大小要大得多。因此,当您仅分配太小的sizeof(element)
字节时。
通过valgrind运行程序很容易发现这种错误。
答案 1 :(得分: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()
中的节点并将其插入列表。将这些功能放在一起可以做:
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)当不再需要它时可以释放。
当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。
对于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)
始终确认已释放已分配的所有内存,并且没有内存错误。
仔细研究一下,如果您有任何疑问,请告诉我。