Error deleting node from first position in c

混江龙づ霸主 提交于 2021-02-07 11:06:31

问题


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!


回答1:


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.



来源:https://stackoverflow.com/questions/36731270/error-deleting-node-from-first-position-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!