Freeing all allocated memory in case of failure

时间:2015-06-15 14:39:45

标签: c memory memory-management malloc

I'm working on a C project (assignment for school). One of the demands is that in case of malloc() failure, the program must free() all allocated memory and exit().

Consider a case where function A() constructs a linked-list and in each iteration it calls to another function, B(). Now, if a malloc failure occured at B(), it must free() the memory it allocated but function A() should do that as well.

Things are getting quite complicated when you have a tree of function calls larger than two.

In my previous project I used a flag to notify a malloc() failure - if a function uses another function which may use malloc(), it has to check the flag right after. It worked, but code got kinda messy.

Is there a neat solution for this problem?
Of course, with "real" applications all memory is de-allocated by the OS, but I guess this demand is pedagogical..

4 个答案:

答案 0 :(得分:2)

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

It will be zero in case of failure, and you check that. And finally, when you go about freeing, you always check the pointer value before calling free():

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

There is no need for an extra flag.

答案 1 :(得分:2)

我认为最简单的方法是创建一个自定义分配器(正如已经在已删除的帖子中提到的那样)来跟踪所有分配,然后执行自定义解除分配器,将它们用于所有堆内存需求。

如果malloc失败,您可以轻松访问以前分配的块列表。

e.g。 (你需要重做这个因为它没有效果,应该进行优化,但要显示原理,只有眼睛编译)

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 :(得分:1)

You could look into the atexit() function, to register code that will be executed when the program terminates. Such code can then check if there is anything that needs to be free()d.

Note that atexit() has no way to unregister. So you need to make sure that you register each cleanup function only 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();
}

答案 3 :(得分:1)

Are you having issue checking for errors or handling them? If you want info on catching them, use donjuedo's suggestion.

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

1) For a uni-directional linked-list, keep a special pointer that points to the head of the list. In your cascading free function, start at the head, capture the next-pointer in a temp variable, free the head, move to the next structure in the list using the 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 keep a special pointer to the head of the list. Assuming you are still at the tail, just capture the previous-pointer into a temp variable, free the tail, move back using the temp-pointer, and repeat the process until the previous-pointer == 0