Freeing all allocated memory on failure

I am working on project C (assignment for school). One of the requirements is that in case of failure, the malloc()

program must free()

allocate all allocated memory and exit()

.

Consider the case where a function A()

creates a linked list, and at each iteration, it calls another function B()

. Now, if there was a malloc error in B()

, it should free()

allocate the allocated memory, but the function A()

should do that too.

Things get pretty complicated when you have more than two function call trees.

In my previous project, I used a flag to notify the malloc () error - if a function uses another function it can use malloc()

, it should check the flag right after. It worked, but the code was no longer useful.

Is there a clear solution to this problem?
Of course, with "real" applications, all memory is allocated by the OS, but I think this requirement is pedagogical.

+3


source to share


4 answers


Yes. The best (and common) way is to initialize each pointer value to zero. Then set it during the malloc () assignment. Example: myPtr = malloc (10);

It will be zero on failure, and you will check it. And finally, when you go to free, you always check the pointer value before calling free ():



if ( myPtr != 0 )
    free( myPtr );

      

No extra flag needed.

+2


source


I think the simplest approach is to create a custom allocator (as someone already pointed out in a remote post) to keep track of all your allocations and then execute a custom deactivator, use them for all your heap memory needs.

If malloc fails you have a list of previously allocated blocks with ease.



eg. (you need to repeat this reason, it is inefficient and needs to be optimized, but shows the principle and only ocular compilation)

typedef struct
{
  void* pMemory; /* for the allocated memory */
  size_t size;   /* for better debugging */

} MemoryBlock;

#define MAXBLOCKS 1000

MemoryBlock myheap[MAXBLOCKS]; // global so zero:ed
static int block = 0;

void* myalloc(size_t size)
{
   static int block = 0;

   // you should check vs MAXBLOCKS 

   myheap[block].pMemory = malloc(size); 
   myheap[block].size = size;   

   // check if it failed.
   if ( myheap[block].pMemory == NULL )
   {
      for (int i = 0; i < block; ++i)
      {
         myfree(myheap[i].pMemory);
      }
      fprintf( stderr, "out of memory\n");
      exit(EXIT_FAILURE);
   }
   else
   {
     return myheap[block++].pMemory;
   }
}

void myfree(void* p)
{
   for (int i = 0; i < block; ++i)
   {
      if ( p == myheap[i].pMemory )
      {
         free(myheap[i].pMemory);
         myheap[i].pMemory = NULL;
         return; 
      }
   }
}

      

+2


source


You can look into the function atexit()

to register the code to be executed when the program exits. Such code can check if there is something that should be free()

d.

Please note that atexit()

there is no way to unregister. Therefore, you need to make sure that you only register each cleanup function once and that it does the right thing when there is nothing to clean up.

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

int *ptr1;
char *ptr2;
int clean1_registered, clean2_registered;

void clean1(void)
{
    printf("clean1 called\n");
    if (ptr1) {
        free(ptr1);
        ptr1 = NULL;
    }
}

void clean2(void)
{
    printf("clean2 called\n");
    if (ptr2) {
        free(ptr2);
        ptr2 = NULL;
    }
}

void B(void)
{
    ptr2 = malloc(100);
    if (!clean2_registered) {
        atexit(clean2);
    }
}

void A(void)
{
    ptr1 = malloc(100 * sizeof(int));
    if (!clean1_registered) {
        atexit(clean1);
    }
    B();
}

int main(int argc, char **argv)
{
    A();
}

      

+1


source


Have a problem checking errors or handling them? If you would like information on how to catch them, use the donjuedo suggestion.

For ideas on freeing memory on error, try one of these two methods:

1) For a unidirectional linked list, keep a special pointer pointing to the head of the list. In your cascading free function, start at the head, grab the next pointer in the temp variable, free the head, move to the next structure in the list with a temp pointer, and repeat the process until the next pointer == 0.

2) For a bi-directional linked list (my preference) you don't need to give a special pointer to the list header. Assuming you're still on the tail, just write the previous pointer into a temporary variable, free the tail, return with the temporary pointer, and repeat the process until the previous pointer == 0

+1


source







All Articles