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

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

问题描述

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

  void deleteNode(card * head,int坐标){卡*当前=头;卡* temp = NULL;temp =当前;int count = 1;while(head!= NULL){如果(坐标== 0){当前=当前-> listp;自由(临时);休息;}否则,如果(count ==坐标){temp =当前-> listp;current-> listp = current-> listp-> listp;自由(临时);休息;}别的 {计数++;当前 = 当前-> listp;}}} 

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

解决方案

如注释中所述,您遇到的问题是由于没有将列表的 address-of 传递给 deleteNode 函数.这个问题是基本的,但它吸引了很多人.链表的地址是第一个节点的地址.因此,在删除第一个节点时,必须将新的列表地址设置为下一个节点地址,以使列表继续运行.

当您将指针传递给函数时,例如 void deleteNode(card * head,... ,函数 deleteNode 会收到指针 head copy .该副本具有一个自己的地址,并且该地址与调用函数中返回的指针的地址无关.在 deleteNode 中,该指针的 value 是相同的和呼叫者一样,但是地址完全不同.

当您删除 deleteNode 中的第一个节点时,会发生此问题.内存是 free 'd,然后返回 deleteNode 函数.现在回到调用者(大概是 main()),下一次您尝试访问 head 时-bam! segfault .为什么? head 的地址从未在 main 中更新,因此它仍指向原始节点-您对中原始节点的内存做了什么> deleteNode ?(您在指向保存第一个节点的内存块的指针上调用了 free -它消失了……)

要解决此问题,只需将列表的 address-of 地址( head )传递给 deleteNode .(例如, void deleteNode(card ** head,... ).然后,您将在 head 地址上进行操作(例如,>> pointer-to-the-pointer-head ).现在,在删除第一个节点之前,您可以设置 * head = head-> listp; 并在其中反映出新的列表地址调用函数( main()).例如,您的代码可以写为:

  void delnode(卡** head,int坐标){卡*当前= *头;卡*受害者= NULL;受害人=当前;int count = 1;while(当前!= NULL){如果(坐标== 0){* head =当前-> listp;免费(受害者);休息;}否则如果(计数==坐标){受害者=当前-> listp;current-> listp = current-> listp-> listp;免费(受害者);休息;}别的 {计数++;当前=当前-> listp;}}} 

但是,您可以以最小的努力对函数的逻辑进行一些改进.例如

  void delnode(卡** head,int坐标){卡*当前= *头;卡*受害者=当前;int count = 1;如果(坐标== 0){* head =当前-> listp;免费(受害者);返回;}while(当前!= NULL){if(count ==坐标){受害者=当前-> listp;current-> listp = current-> listp-> listp;免费(受害者);返回;}计数++;当前=当前-> listp;}} 

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

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

话虽如此,您可以通过少量示例代码来确认删除操作.

  #include< stdio.h>#include< stdlib.h>typedef结构卡{int cardno;结构卡* listp;} 卡片;卡* createnode(int c);card * insert(card ** list,int c);无效的prnlist(卡*列表);无效的delnode(卡** head,int坐标);无效dellist(卡* list);无效* xcalloc(size_t nmemb,size_t sz);int main(void){卡*列表= NULL;插入(& list,18);/*插入测试节点*/插入(& list,6);插入(& list,54);插入 (&list, 12);插入(& list,60);插入(& list,30);printf("\ noriginal list:\ n");prnlist(列表);printf("\ ndeleting节点:2 \ ndeleting节点:0 \ n");delnode(& list,2);/*删除第3个&第一节点*/delnode(& list,0);printf("\ n最终列表:\ n");prnlist(列表);删除列表(list);/*可用的已分配内存*/返回0;}卡* createnode(int c){card * node = xcalloc(1,sizeof * node);node-> listp = NULL;节点-> cardno = c;返回节点;}卡*插入(卡**清单,整数c){卡* iter = *列表;卡* node = createnode(c);如果(!* list){/*将第一个节点添加到列表*/*列表=节点;返回* list;}/*在末尾插入所有其他节点*/for(; iter-> listp; iter = iter-> listp){}iter-> listp =节点;返回* list;}无效的prnlist(卡*列表){卡 *iter = 列表;对于(; iter-> listp; iter = iter-> listp)printf("cardno:%d \ n",iter-> cardno);printf("cardno:%d \ n",iter-> cardno);}无效的delnode(卡** head,int坐标){卡*当前= *头;卡*受害者=当前;int count = 1;如果(坐标== 0){* head =当前-> listp;免费(受害者);返回;}while(当前!= NULL){如果(计数==坐标){受害者=当前-> listp;current-> listp = current-> listp-> listp;免费(受害者);返回;}计数++;当前=当前-> listp;}}无效dellist(卡*列表){卡* iter =清单;while(iter){卡*受害者=重复;iter = iter-> listp;免费(受害者);}}无效* xcalloc(size_t nmemb,size_t sz){无效* memptr = calloc(nmemb,sz);如果(!memptr){fprintf(stderr,"xcalloc()错误:虚拟内存已耗尽.\ n");退出(EXIT_FAILURE);}退货} 

使用/输出示例

  $ ./bin/lldelcard原始清单:卡片编号:18卡号:6卡诺:54卡号:12卡片编号:60卡号:30删除节点:2删除节点:0最终名单:卡号:6卡号:12卡片编号:60卡号:30 

内存错误检查

在您的任何动态分配内存的代码中,您对分配的任何内存块都有2种责任:(1)始终保留指向该内存块起始地址的指针,因此,(2)在以下情况下可以释放该指针:不再需要它.

当务之急是使用一个内存错误检查程序来确保您没有在所分配的内存块之外/之外进行写操作,试图读取或基于一个非初始化值进行跳转,最后确认您已释放了所有内存您分配的内存.

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

  $ valgrind ./bin/lldelcard== 9094 == Memcheck,内存错误检测器== 9094 ==朱利安·塞沃德(Julian Seward)等人的版权(C)2002-2013和GNU GPL.== 9094 ==使用Valgrind-3.10.1和LibVEX;使用-h重新运行以获取版权信息== 9094 ==命令:./bin/lldelcard== 9094 ==原始清单:卡片编号:18卡号:6卡诺:54卡号:12卡片编号:60卡号:30删除节点:2删除节点:0最终名单:卡号:6卡号:12卡片编号:60卡号:30== 9094 ==== 9094 ==堆摘要:== 9094 ==在出口处使用:0字节,0块== 9094 ==总堆使用量:6个分配,6个释放,96个字节分配== 9094 ==== 9094 ==释放了所有堆块-不可能泄漏== 9094 ====9094== 对于检测到和抑制的错误的计数,重新运行: -v== 9094 ==错误摘要:0个上下文中的0个错误(禁止:1个中的1个) 

始终确认所有堆块均已释放-不可能泄漏,并且同样重要的错误摘要:来自0个上下文的0个错误.

祝您编码顺利.

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

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!

解决方案

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.

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.

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...)

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.

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 ("\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;
}

Example Use/Output

$ ./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

Memory Error Check

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.

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)

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

Good luck with your coding.

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

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