Search code examples
cmemory-leaksvalgrindsingly-linked-list

C: Memory leak implementing a simple linked list


I created a linked list that stores integers. The program appears to run fine but Valgrind informs me that there is a memory leak. I am not sure how this is possible. The code is provided below along with the output and Valgrinds assesment. Thank you.

main.c

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

int main( int argc, char* argv[ ] ){
    int num = 0;
    NODE head = NULL;

    num = 7;

    head = list_insert( head, num );
    bytes_of_list( head );

    head = list_insert( head, 9 );
    bytes_of_list( head );

    head = list_insert( head, 2 );
    bytes_of_list( head );

    head = list_insert( head, 8 );
    bytes_of_list( head );

    delete_node( head, 6 );
    delete_node( head, 9 );
    bytes_of_list( head );

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

    linked_list_destroy( &head );
    bytes_of_list( head );

    return 0;
}

linked_list.c

#include <stdlib.h>
#include <stdio.h>
#include "linked_list.h"
#include "status.h"

struct node;
typedef struct node Node;
struct node{
    int data;
    Node* next;
};
typedef struct node Node;

/************************************************************** list insert */
NODE list_insert( NODE head, int data ){
    Node* pNode = NULL;

    printf( "\nInsert %d into list.\n", data );

    pNode = ( Node* )malloc( sizeof( Node ));
    if( !pNode ) exit( 1 );
    pNode->data = data;
    pNode->next = head;
    return pNode;
}
/******************************************************  linked_list_destroy */
void linked_list_destroy( NODE* head ){
    Node* phead = ( Node* )*head;
    Node* prevNode = NULL;

    printf( "\nDestroy List:\n");

    if( !phead ) return;
    while( phead != NULL ){
        prevNode = phead;
        phead = phead->next;
        printf( "Deleting %d\n", prevNode->data );
        prevNode->data = 0;
        prevNode->next = NULL;
        free( prevNode );
    }
    *head = NULL;
}
/***************************************************************  print_list */
void print_list( NODE head ){
    Node* pHead = ( Node* )head;

    printf( "\nPrint list:\n");

    while( pHead != NULL ){
        printf( "%d ", pHead->data );
        pHead = pHead->next;
    }
}
/***********************************************************  delete nodes */
void delete_node( NODE head, int data ){
    Node* phead = ( Node* )head;
    Node* prev = NULL;

    printf( "\nDelete %d from list:\n", data );

    if( !head ) return;
    while(( phead != NULL ) && ( phead->data != data )){
        prev = phead;
        phead = phead->next;
    }
    if( !phead ) printf( "Sorry, %d is not in the list.\n", data);
    else{
        prev->next = phead->next;
        free( phead );
    }
    return;
}
/********************************************************* bytes of list */
int bytes_of_list( NODE head ){
    Node* phead = ( Node* )head;
    int bytes_total = 0;
    while( phead != NULL ){
        bytes_total += sizeof( *phead );
        phead = phead->next;
    }
    printf( "The current size of the list is %d bytes.\n", bytes_total );
    return bytes_total;
}

linked_list.h

#ifndef LINKED_LIST_H_INCLUDED
#define LINKED_LIST_H_INCLUDED
#include "status.h"

typedef void* NODE;
NODE list_insert( NODE head, int data );
void print_list( NODE head );
void linked_list_destroy( NODE* head );
void delete_node( NODE head, int data );
Status in_list( NODE head, int data );
int bytes_of_list( NODE head );

#endif

status.h

#ifndef STATUS_H_INCLUDED
#define STATUS_H_INCLUDED
enum status {FALSE, TRUE};
typedef enum status Status;
#endif

Output for this program is as follows:

Insert 7 into list.

The current size of the list is 16 bytes.

Insert 9 into list.

The current size of the list is 32 bytes.

Insert 2 into list.

The current size of the list is 48 bytes.

Insert 8 into list.

The current size of the list is 64 bytes.

Delete 6 from list:

Sorry, 6 is not in the list.

Delete 9 from list:

The current size of the list is 48 bytes.

Print list:

8 2 7

Destroy List:

Deleting 8

Deleting 2

Deleting 7

The current size of the list is 0 bytes.

VALGRIND OUTPUT:

==2758== HEAP SUMMARY:

==2758== in use at exit: 140,089 bytes in 1,198 blocks

==2758== total heap usage: 1,968 allocs, 770 frees, 283,758 bytes allocated

==2758==

==2758== LEAK SUMMARY:

==2758== definitely lost: 10 bytes in 1 blocks

==2758== indirectly lost: 0 bytes in 0 blocks

==2758== possibly lost: 0 bytes in 0 blocks

==2758== still reachable: 140,079 bytes in 1,197 blocks

==2758== suppressed: 0 bytes in 0 blocks

==2758== Rerun with --leak-check=full to see details of leaked memory

==2758==

==2758== For counts of detected and suppressed errors, rerun with: -v

==2758== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)


Solution

  • Here is a version of the posted code

    1. all jammed into a single file
    2. with all the suggested fixes:
    3. that cleanly compiles
    4. that informs the user when an error occurs

    And now, the proposed version of the code:

    #ifndef STATUS_H_INCLUDED
    #define STATUS_H_INCLUDED
    enum status {FALSE, TRUE};
    typedef enum status Status;
    #endif
    
    
    
    #ifndef LINKED_LIST_H_INCLUDED
    #define LINKED_LIST_H_INCLUDED
    //include "status.h"
    #include <stdio.h>
    
    struct node 
    { 
        int data; 
        struct node *next; 
    }; 
    typedef struct node Node;
    
    Node* list_insert( Node *head, int data );
    void print_list( Node *head );
    void linked_list_destroy( Node** head );
    void delete_node( Node** head, int data );
    Status in_list( Node* head, int data );
    void bytes_of_list( Node* head );
    
    #endif
    
    
    #include <stdio.h>
    #include <stdlib.h>
    //#include "linked_list.h"
    
    int main( void )
    {
        int num = 0;
        Node *head = NULL;
    
        num = 7;
    
        head = list_insert( head, num );
        bytes_of_list( head );
    
        head = list_insert( head, 9 );
        bytes_of_list( head );
    
        head = list_insert( head, 2 );
        bytes_of_list( head );
    
        head = list_insert( head, 8 );
        bytes_of_list( head );
    
        delete_node( &head, 6 );
        delete_node( &head, 9 );
        bytes_of_list( head );
    
        print_list( head );
        printf( "\n" );
    
        linked_list_destroy( &head );
        bytes_of_list( head );
    
        return 0;
    }
    
    
    //#include <stdlib.h>
    //#include <stdio.h>
    //#include "linked_list.h"
    //#include "status.h"
    
    
    
    
    /************************************************************** list insert */
    Node *list_insert( Node *head, int data )
    {
        printf( "\nInsert %d into list.\n", data );
    
        Node *pNode = malloc( sizeof( Node ));
        if( !pNode )
        {
            perror( "malloc failed" );
            exit( 1 );
        }
    
        pNode->data = data;
        pNode->next = head;
        return pNode;
    }
    
    
    /******************************************************  linked_list_destroy */
    void linked_list_destroy( Node** head )
    {
        Node* phead = *head;
        Node* prevNode = NULL;
    
        printf( "\nDestroy List:\n");
    
    
        while( phead )
        {
            prevNode = phead;
            phead = phead->next;
            printf( "Deleting %d\n", prevNode->data );
            prevNode->data = 0;
            prevNode->next = NULL;
            free( prevNode );
        }
        *head = NULL;
    }
    
    
    /***************************************************************  print_list */
    void print_list( Node *head )
    {
        Node* pHead = head;
    
        printf( "\nPrint list:\n");
    
        while( pHead )
        {
            printf( "%d ", pHead->data );
            pHead = pHead->next;
        }
    }
    
    
    /***********************************************************  delete nodes */
    void delete_node( Node **head, int data )
    {
        Node* phead = *head;
        Node* prev  = NULL;
    
        printf( "\nDelete %d from list:\n", data );
    
        //if( !head ) return;
    
        while(( phead ) && ( phead->data != data ))
        {
            prev = phead;
            phead = phead->next;
        }
    
        if( !phead ) 
        {
            printf( "Sorry, %d is not in the list.\n", data);
        }
    
        else
        {
            prev->next = phead->next;
            free( phead );
        }
        return;
    }
    
    
    /********************************************************* bytes of list */
    void bytes_of_list( Node* head )
    {
        Node* phead = head;
        size_t bytes_total = 0;
    
        while( phead )
        {
            bytes_total += sizeof( *phead );
            phead = phead->next;
        }
    
        printf( "The current size of the list is %lu bytes.\n", bytes_total );
        //return bytes_total;
    }