如 cmets 中所述,您遇到的问题是由于没有将列表的 address-of 传递给 deleteNode 函数。这个问题是基本的,但它吸引了很多人。链表的地址是第一个节点的地址。所以在删除第一个节点时,必须给下一个节点地址设置一个新的列表地址,才能让列表继续运行。
当你传递一个指向函数的指针时,例如void deleteNode(card *head, ...,函数deleteNode 接收指针head 的副本。副本有一个自己的地址,该地址与调用函数中指针的地址无关。 deleteNode中指针的值与调用者中相同,但地址完全不同。
当您删除deleteNode 中的第一个节点时会出现问题。内存为free'd,deleteNode 函数返回。现在回到调用者(大概是main()),下次您尝试访问head 时——bam! 段错误。为什么? head 的地址从未在main 中更新,所以它仍然指向原始节点——您刚刚对deleteNode 中原始节点的内存做了什么? (你在指向持有第一个节点的内存块的指针上调用了free——它已经消失了......)
要解决此问题,只需将列表 (head) 的地址 传递给deleteNode。 (例如void deleteNode(card **head, ...)。然后你在head的地址上操作(例如一个pointer-to-the-pointer-head)。现在,在删除第一个节点之前,您可以设置 *head = head->listp; 并将新的列表地址反映在调用函数 (main()) 中。例如,您的代码可以写成:
void delnode (card **head, int coordinate)
{
card *current = *head;
card *victim = NULL;
victim = current;
int count = 1;
while (current != NULL) {
if (coordinate == 0) {
*head = current->listp;
free (victim);
break;
}
else if (count == coordinate) {
victim = current->listp;
current->listp = current->listp->listp;
free (victim);
break;
}
else {
count++;
current = current->listp;
}
}
}
但是,您可以轻松地对函数的逻辑进行一些改进。例如
void delnode (card **head, int coordinate)
{
card *current = *head;
card *victim = current;
int count = 1;
if (coordinate == 0) {
*head = current->listp;
free (victim);
return;
}
while (current != NULL)
{
if (count == coordinate) {
victim = current->listp;
current->listp = current->listp->listp;
free (victim);
return;
}
count++;
current = current->listp;
}
}
最后,访问描述How to Ask a Question 和How to create a Minimal, Complete, and Verifiable example 的链接。提供必要的详细信息,包括您的代码和相关错误(如果有),将让这里的每个人都能帮助您解决问题。
这个问题就是一个很好的例子。对于帮助您并实际编译和确认问题或答案的任何人,您要求这里的人们编写一个示例程序,该程序对您的潜在列表结构可能是什么做出有根据的猜测。当您在这里提出问题时,提供 MCVE 的目的是让其他人可以编译您的代码并确认您遇到的问题,如果需要,通过调试器运行编译后的代码以帮助您。如果您遵循帮助我们帮助您的网站的最低建议和规则,您将获得更多帮助和更多积极响应。
话虽如此,你可以用这个小示例代码来确认你的删除操作。
#include <stdio.h>
#include <stdlib.h>
typedef struct card {
int cardno;
struct card *listp;
} card;
card *createnode (int c);
card *insert (card **list, int c);
void prnlist (card *list);
void delnode (card **head, int coordinate);
void dellist (card *list);
void *xcalloc (size_t nmemb, size_t sz);
int main (void) {
card *list = NULL;
insert (&list, 18); /* insert test nodes */
insert (&list, 6);
insert (&list, 54);
insert (&list, 12);
insert (&list, 60);
insert (&list, 30);
printf ("\noriginal list:\n");
prnlist (list);
printf ("\ndeleting node: 2\ndeleting node: 0\n");
delnode (&list, 2); /* delete 3rd & 1st nodes */
delnode (&list, 0);
printf ("\nfinal list:\n");
prnlist (list);
dellist (list); /* free allocated memory */
return 0;
}
card *createnode (int c)
{
card *node = xcalloc (1, sizeof *node);
node->listp = NULL;
node->cardno = c;
return node;
}
card *insert (card **list, int c)
{
card *iter = *list;
card *node = createnode (c);
if (!*list) { /* add 1st node to list */
*list = node;
return *list;
}
/* insert all other nodes at end */
for (; iter->listp; iter = iter->listp) {}
iter->listp = node;
return *list;
}
void prnlist (card *list)
{
card *iter = list;
for (; iter->listp; iter = iter->listp)
printf (" cardno : %d\n", iter->cardno);
printf (" cardno : %d\n", iter->cardno);
}
void delnode (card **head, int coordinate)
{
card *current = *head;
card *victim = current;
int count = 1;
if (coordinate == 0) {
*head = current->listp;
free (victim);
return;
}
while (current != NULL)
{
if (count == coordinate) {
victim = current->listp;
current->listp = current->listp->listp;
free (victim);
return;
}
count++;
current = current->listp;
}
}
void dellist (card *list)
{
card *iter = list;
while (iter) {
card *victim = iter;
iter = iter->listp;
free (victim);
}
}
void *xcalloc (size_t nmemb, size_t sz)
{
void *memptr = calloc (nmemb, sz);
if (!memptr) {
fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
return memptr;
}
使用/输出示例
$ ./bin/lldelcard
original list:
cardno : 18
cardno : 6
cardno : 54
cardno : 12
cardno : 60
cardno : 30
deleting node: 2
deleting node: 0
final list:
cardno : 6
cardno : 12
cardno : 60
cardno : 30
内存错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以在以下情况下被释放它不再需要。
您必须使用内存错误检查程序来确保您没有写入超出/超出分配的内存块,尝试读取或基于未初始化的值进行跳转,最后确认您已释放所有您分配的内存。
对于 Linux,valgrind 是正常的选择。有许多微妙的方法可以滥用新的内存块。使用内存错误检查器可以让您识别任何问题并验证您分配的内存的正确使用,而不是通过segfault 发现问题存在。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/lldelcard
==9094== Memcheck, a memory error detector
==9094== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==9094== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==9094== Command: ./bin/lldelcard
==9094==
original list:
cardno : 18
cardno : 6
cardno : 54
cardno : 12
cardno : 60
cardno : 30
deleting node: 2
deleting node: 0
final list:
cardno : 6
cardno : 12
cardno : 60
cardno : 30
==9094==
==9094== HEAP SUMMARY:
==9094== in use at exit: 0 bytes in 0 blocks
==9094== total heap usage: 6 allocs, 6 frees, 96 bytes allocated
==9094==
==9094== All heap blocks were freed -- no leaks are possible
==9094==
==9094== For counts of detected and suppressed errors, rerun with: -v
==9094== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
始终确认所有堆块都已释放——不可能有泄漏,同样重要的是错误摘要:0 个上下文中的 0 个错误。
祝你编码顺利。