简体   繁体   中英

doubly linked list freeing memory valgrind error

Hi I'm trying to free the memory that I was allocated in the doubly linked list but when I check it with valgrind I have some error in the free_all function ( I think ) but I don't know how to avoid it.

I think in the free_all function I'm using temp and node pointer wrong or I need to allocate it first and then use them, but when I've tried this method valgrind still gave me some error.

#include <stdio.h>
#include <stdlib.h>

/*
  to compile it:
  gcc -g -Wall -ggdb3  double_linkedlist2.c -o double_linkedlist
  to check for memory leak and error:
  valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt ./double_linkedlist
*/
typedef struct listitem
{
  struct listitem *next;    // pointer to next item
  struct listitem *prev;    // pointer to previous item
  int data;                 // some data
} ITEM;


int main (void)
{
  // prototype functions
  void free_all (ITEM *lst_ptr);


  // Variables
  ITEM *p_temp, *head;

  head = malloc (sizeof (ITEM));  // head will keep first and last element in its pointers
  head -> next = head;            // the last element in the list (at first head -> next and head -> prev will point to the head)
  head -> prev = head;            // the first element in the list


     for (int i = 0; i < 3; i++)
       {
         p_temp = malloc (sizeof (ITEM));     // allocate some memory for the new list item
         p_temp -> data = i;                  // set the list item's data to the loop count so that we can see where it is in the list
         p_temp -> next = head -> next;          // this will insert at the FRONT of the list
         head -> next = p_temp;                  // and set the list head to the newly created list item
         p_temp -> prev = head;              // this will insert at the BACK of the list
         p_temp -> next -> prev = p_temp;       // and set the list 'tail' to the newly created item
       }

     // now let's see what we got going forward
     printf ("Going forward\n");
     p_temp = head -> next;

     while (p_temp != head)
       {
         printf ("forward list item: current is %p; next is %p; prev is %p; data is %d\n", p_temp, p_temp -> next, p_temp -> prev, p_temp -> data);
         p_temp = p_temp -> next;
       }

     // now let's see what we got going backward
     printf ("Going backwards\n");
     p_temp = head -> prev;

     while (p_temp != head)
       {
         printf ("backward list item; current is %p; next is %p; prev is %p; data is %d\n", p_temp, p_temp -> next, p_temp -> prev, p_temp -> data);
         p_temp = p_temp -> prev;
       }

     printf ("\n");
     free_all (head);

     return 0;
}

void free_all (ITEM *head)
{
  ITEM *temp, *node;

  node = head;

  while (node != head -> prev)
    {
      temp = node;
      printf ("freed list item: current is %p; next is %p; prev is %p; data is %d\n", temp, temp -> next, temp -> prev, temp -> data);
      node = node -> next;
      free (temp);
    }
  free (node);
  free (head);
}

your free_all has at least two errors: the while condition references head->prev, but in the first iteration you free head (use after free). after you exit the loop, you free head despite having free'd it in the first iteration. free_all() does work for the single element case.

There was no error or memory leak in valgrind after this modification

void free_all (ITEM *head)
{
  ITEM *temp, *node = NULL;

  node = head -> next;

  while (node != head -> prev)
    {
      temp = node;
      printf ("freed list item: current is %p; next is %p; prev is %p; data is %d\n", node, node -> next, node -> prev, node -> data);
      node = node -> next;
      free (temp);
    }

  node = head -> prev;
  printf ("freed list item: current is %p; next is %p; prev is %p; data is %d\n", node, node -> next, node -> prev, node -> data);
  free (head);
  free (node);
}

This post was written by mevets as an edit to my solution but I thought it would be better to include this to the thread as well:

mevets:

I would tend towards something like:

void Unlink(ITEM **head, ITEM *t) {
        if (t->next == t) {
                /* remove head */
                *head = NULL;
        } else {
                t->prev->next = t->next;
                t->next->prev = t->prev;
                if (t == *head) {
                        *head = t->next;
                }
        }
}

/*
   remove and return the element after head
*/
ITEM *Pop(ITEM **head) {
        ITEM *node;
        if ((node = *head) != NULL) {
                node = node->next;
                Unlink(head, node);
        }
        return node;
}


void free_all (ITEM *head) {
        ITEM *node;
        while ((node = Pop(&head)) != NULL) {
            free(node);
        }
}

which separates out the list maintenance (Unlink) from the ordering (Pop) and the memory management (free_all). This leaves you with more boundaries where you could make assertions about the list, for examaple pre and post Unlink, the list should be valid and could be checked. Also, if there was concurrent access to the list, you could bracket Pop() with a lock, to minimize conflict.

A caching allocator is a thing itself, but a typical part of the contract is you release nodes in a known state so it can skip the construction of them when they are re-allocated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM