Search code examples
cstructfree

Deallocating memory for array within a linked chain struct


I am trying to deallocate the memory used by different struct that are part of a linked chain. The dumpGroupingOrder() function is supposed to deallocate the memory used, however the whole program ceases to function once it is used. Without it the program works even though there are memory leaks, see the output of valgrind below:

LEAK SUMMARY:
definitely lost: 48 bytes in 1 blocks
indirectly lost: 328 bytes in 11 blocks

With it there are no memory leaks, but I get a double free or corruption (out) error message instead and the following error message from valgrind:

Invalid free() / delete / delete[] / realloc()
   at 0x48399AB: free (vg_replace_malloc.c:538)
   by 0x10929A: dumpGroupingOrder (code-stack.c:54)
   by 0x10952F: main (code-stack.c:88)
   Address 0x1fff0000b0 is on thread 1's stack
   in frame #2, created by main (code-stack.c:74)

Below the entire code used as a MWE:

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


struct groupingOrder {
  int *taxa;
  size_t groupeSize;
  long double distance;
  struct groupingOrder* next;
};


void addGroup( struct groupingOrder* HEAD, int * groupe, size_t groupeSize, long double distance ) {

  struct groupingOrder* temp = HEAD;
  // Moving the last unit structure
  while( temp->next != NULL ) {
    temp = temp->next;
  }
  // Allocating space for the unit structure
  struct groupingOrder* nouveau = malloc( sizeof( struct groupingOrder ) );
  nouveau->distance = distance;
  nouveau->groupeSize = groupeSize;
  // Allocating variable space for the list of taxa to be stored
  nouveau->taxa = malloc( groupeSize * sizeof( int ) );
  for( int i = 0; i < groupeSize; i++) {
    nouveau->taxa[i] = groupe[i];
  }
  // Linking the new unit structure to the previous one
  temp->next = nouveau;
  // Specifying the NULL pointer to label the last unit
  nouveau->next = NULL;
}


void dumpGroupingOrder( struct groupingOrder* HEAD ) {

  struct groupingOrder* temp = HEAD;
  while( temp != NULL ) {

    // Copying the address of the current unit
    struct groupingOrder* dumpTarget = temp;
    // Storing the address of the next unit before dumping the
    //  current one.
    temp = temp->next;

    // Dumping the current unit and all its allocated variables
    if( dumpTarget->taxa != NULL ) {
      free( dumpTarget->taxa );
      printf( " #" );
    }
    free( dumpTarget );
    printf( "* " );
  }
}


void showGroupingOrder( struct groupingOrder* HEAD ) {

  struct groupingOrder* temp = HEAD;
  while( temp->next != NULL ) {
    temp = temp->next;
    for( int i = 0; i < temp->groupeSize; i++) {
      printf( " %d", temp->taxa[i] );
    }
    printf( " %Lf\n", temp->distance );
  }
}



int main() {
  
  size_t groupeSize[6] = { 2,2,4,2,5,7 };
  int groupes[][7] = { {2,3}, {5,1}, {2,3,5,1}, {7,4}, {2,3,5,1,6}, {2,3,5,1,6,7,4} };

  struct groupingOrder HEAD = { .next = NULL };
  
  for( int i = 0; i < 6; i++ ){

    addGroup( &HEAD, groupes[i], groupeSize[i], 0.0003 );
  }

  showGroupingOrder( &HEAD );
  printf( "\n\n" );
  dumpGroupingOrder( &HEAD );
  
  return 0;
}

Solution

  • As already pointed out in the other answer, the problem is that the head node of your linked list was not allocated by malloc. Therefore, you are not allowed to call free on that node.

    However, in contrast to the other answer, I do not recommend that you fix the problem by leaving out the head node when freeing the memory, but rather to restructure your program so that all nodes (including the head node) are treated equally, so that also the memory for the head node gets allocated by malloc:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    
    struct groupingOrder {
        int *taxa;
        size_t groupeSize;
        long double distance;
        struct groupingOrder* next;
    };
    
    
    void addGroup( struct groupingOrder** HEAD, int * groupe, size_t groupeSize, long double distance ) {
    
        struct groupingOrder** temp = HEAD;
        // Make "temp" point to the "next" member of the last node of
        // the list, or if the list is empty, make "temp" point to
        // the pointer to the head node (which has the value NULL).
        while( (*temp) != NULL ) {
            temp = &(*temp)->next;
        }
        // Allocating space for the unit structure
        struct groupingOrder* nouveau = malloc( sizeof( struct groupingOrder ) );
        nouveau->distance = distance;
        nouveau->groupeSize = groupeSize;
        // Allocating variable space for the list of taxa to be stored
        nouveau->taxa = malloc( groupeSize * sizeof( int ) );
        for( int i = 0; i < groupeSize; i++) {
            nouveau->taxa[i] = groupe[i];
        }
        // Specifying the NULL pointer to label the last unit
        nouveau->next = NULL;
        // Linking the new unit structure to the previous one
        *temp = nouveau;
    }
    
    
    void dumpGroupingOrder( struct groupingOrder** HEAD ) {
    
        struct groupingOrder* temp = *HEAD;
        while( temp != NULL ) {
    
            // Copying the address of the current unit
            struct groupingOrder* dumpTarget = temp;
            // Storing the address of the next unit before dumping the
            //  current one.
            temp = temp->next;
    
            // Dumping the current unit and all its allocated variables
            if ( dumpTarget->taxa != NULL ) {
                free( dumpTarget->taxa );
                printf( " #" );
            }
            free( dumpTarget );
            printf( "* " );
        }
    
        // Mark linked list as empty
        *HEAD = NULL;
    }
    
    
    void showGroupingOrder( struct groupingOrder* HEAD ) {
    
        for ( struct groupingOrder* temp = HEAD; temp != NULL; temp = temp->next ) {
            
            for( int i = 0; i < temp->groupeSize; i++) {
                printf( " %d", temp->taxa[i] );
            }
            printf( " %Lf\n", temp->distance );
        }
    }
    
    
    
    int main() {
      
        size_t groupeSize[6] = { 2,2,4,2,5,7 };
        int groupes[][7] = { {2,3}, {5,1}, {2,3,5,1}, {7,4}, {2,3,5,1,6}, {2,3,5,1,6,7,4} };
    
        struct groupingOrder *HEAD = NULL;
      
        for ( int i = 0; i < 6; i++ ) {
    
            addGroup( &HEAD, groupes[i], groupeSize[i], 0.0003 );
        }
    
        showGroupingOrder( HEAD );
        printf( "\n\n" );
        dumpGroupingOrder( &HEAD );
      
        return 0;
    }
    

    It was necessary for me to modify the function main and addGroup. I also modified the other two functions, but these changes were not necessary.

    This program has the following output:

     2 3 0.000300
     5 1 0.000300
     2 3 5 1 0.000300
     7 4 0.000300
     2 3 5 1 6 0.000300
     2 3 5 1 6 7 4 0.000300
    
    
     #*  #*  #*  #*  #*  #*