从 c 中的第一个位置删除节点时出错 [英] Error deleting node from first position in c

查看:17
本文介绍了从 c 中的第一个位置删除节点时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正如我之前的许多帖子所示,我正在编写代码来模拟疯狂的 8 人纸牌游戏.我有一个删除节点功能,用于从正在播放的牌组中删除卡片.它适用于第一张卡片,但每次我尝试从列表中删除第一张卡片(节点)时,它都不会删除,然后在它之后弄乱整个程序.这是函数:

As my many previous posts show, I am making a code to simulate a crazy 8's card game. I have a delete node function that is meant to delete the card from the deck being played. It works for cards after the first, but every time i try to delete the first card (node) from the list it will not delete and then messes up the whole program after it. Here is the function:

void deleteNode(card *head, int coordinate) {

    card *current = head;
    card *temp = NULL;
    temp = current;
    int count = 1;
    while (head != NULL) {
        if (coordinate == 0) {
            current = current->listp;
            free(temp);
            break;
        }
        else if (count == coordinate) {
            temp = current->listp;
            current->listp = current->listp->listp;
            free(temp);
            break;
        }
        else {
            count++;
            current = current->listp;
        }
    }
}

传入它的 *head 是正在玩的手的顶部.坐标是用户想要玩的牌数.例如,如果他们牌组中的第一张牌是红心 Q,而这正是他们想要玩的,那么他们会输入一张.在函数调用中,我从用户选择中减去 1,因此它将与列表匹配(因为列表从 0 开始).任何帮助,将不胜感激.在解决这个问题之前,我无法继续我的项目!

The *head passed into it is the top of the hand being played. The coordinate is the number of card the user wants to play. For example, if the first card in their deck is Q of Hearts and that's what they want to play, they would enter one. In the function call, I subtract one from the user choice so it will match up with the list (since the list starts at 0). Any help would be appreciated. I can't move on with my project until I get this problem resolved!

推荐答案

正如评论中提到的,您遇到的问题是由于没有将 address-of 列表传递给 deleteNode 函数.这个问题很基本,但它抓住了很多人.链表的地址,是第一个节点的地址.所以在删除第一个节点时,必须给下一个节点地址设置一个新的链表地址,链表才能继续运行.

As mentioned in the comments, the problem you are running into is due to not passing the address-of the list to the deleteNode function. The problem is basic, but it catches a lot of people. The address of a linked-list, is the address of the first node. So when deleting the first node, you must set a new list address to the next node address in order for the list to continue to operate.

当你传递一个指向函数的指针时,例如void deleteNode(card *head, ...,函数deleteNode接收一个copy的指针head.副本有一个自己的地址,该地址与调用函数中指针的地址无关.指针的deleteNode中是相同的和调用者一样,但地址完全不同.

When you pass a pointer to a function, e.g. void deleteNode(card *head, ..., the function deleteNode receives a copy of the pointer head. The copy has an address all its own and that address has no relation to the address of the pointer back in the calling function. The value of the pointer is the same in deleteNode as it is in the caller, but the address is completely different.

问题发生在删除deleteNode中的第一个节点.内存是free'd,deleteNode 函数返回.现在回到调用者中(大概是 main()),下次你尝试访问 head 时——砰!段错误.为什么?head 的地址从未在 main 中更新,所以它仍然指向原始节点——你刚刚对 删除节点?(你在一个指向保存第一个节点的内存块的指针上调用了 free —— 它消失了......)

The problem occurs when you delete the first node in deleteNode. The memory is free'd, and the deleteNode function returns. Now back in the caller (presumably main()), the next time you attempt to access head -- bam! segfault. Why? The address for head was never updated in main, so it still points to the original node -- and what did you just do to the memory for the original node in deleteNode? (you called free on a pointer that pointed to the block of memory holding the first node -- its gone...)

要解决这个问题,只需将列表的地址(head)传递给deleteNode.(例如void deleteNode(card **head, ...).那么你正在操作的地址 head(例如一个pointer-to-the-pointer-head).现在在删除第一个节点之前,您可以设置 *head = head->listp; 并将新的列表地址反映回调用函数(main()).例如,你的代码可以写成:

To fix the problem, simply pass the address-of the list (head) to deleteNode. (e.g. void deleteNode(card **head, ...). Then you are operating on the address of head (e.g. a pointer-to-the-pointer-head). Now before deleting the first node you can set *head = head->listp; and have the new list address reflected back in the calling function (main()). For example, your code could be written as:

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

但是,您可以轻松地对函数的逻辑进行一些改进.例如

However, you can make a few improvements to the logic of the function, with minimal effort. e.g.

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

最后,访问描述如何提问如何创建最小、完整且可验证的示例.提供必要的详细信息(包括您的代码和相关错误(如果有))将使这里的每个人都能帮助您解决问题.

Lastly, visit the links describing How to Ask a Question and How to create a Minimal, Complete, and Verifiable example. Providing the necessary details, including your code, and associated errors, if any, will allow everyone here to help you with your question.

这个问题就是一个很好的例子.对于任何帮助您并实际编译和确认问题或答案的人,您要求这里的人编写一个示例程序,对您的底层列表结构可能是什么进行有根据的猜测.当您在这里提出问题时,提供 MCVE 的目的是让其他人可以编译您的代码并确认您遇到的问题,如果需要,通过调试器运行已编译的代码以帮助您.如果您遵循网站的最低建议和规则来帮助我们为您提供帮助,您将获得更多帮助和更多积极响应.

This question is a perfect example. For anyone to help you and actually compile and confirm the problem or answer, you are asking the folks here to write a sample program that makes an educated guess at what your underlying list structure presumably is. When you ask a question here, the purpose of providing a MCVE is so that others may compile your code and confirm the problem you are having, and if need be, run the compiled code through a debugger in order to help you. You will get much more help and much more of a positive response if you follow the minimal suggestions and rule of the site that are there to help us help you.

话虽如此,您可以通过这一小段示例代码来确认您的删除操作.

That being said, you can confirm the operation of your delete with this small bit of sample code.

#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 ("
original list:
");
    prnlist (list);

    printf ("
deleting node: 2
deleting node: 0
");
    delnode (&list, 2);     /* delete 3rd & 1st nodes */
    delnode (&list, 0);

    printf ("
final list:
");
    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
", iter->cardno);
    printf (" cardno : %d
", 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.
");
        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) 可以在以下情况下释放它不再需要它.

In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

您必须使用内存错误检查程序来确保您没有在分配的内存块之外/之外写入,尝试读取或基于未初始化的值进行跳转,并最终确认您已释放所有你分配的内存.

It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.

对于 Linux valgrind 是正常的选择.有许多微妙的方法可以滥用新的内存块.使用内存错误检查器可以让您识别任何问题并验证您分配的内存的正确使用,而不是通过 segfault 发现问题存在.每个平台都有类似的内存检查器.它们都易于使用,只需通过它运行您的程序即可.

For Linux valgrind is the normal choice. There are many subtle ways to misuse a new block of memory. Using a memory error checker allows you to identify any problems and validate proper use of of the memory you allocate rather than finding out a problem exists through a segfault. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ 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个错误.

Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.

祝你编码顺利.

这篇关于从 c 中的第一个位置删除节点时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆