Tips and Tricks to avoid memory leaks in writing C

5 points by naughtysriram ↗ HN
When I write C I used to prototype the model first, then fix the memory leaks. I think its OK for throw code. I find that these leaks are mostly due to patterns of code that one avoids.

Like,

1. not assigning null to a pointer after freeing it

2. not allocating enough memory for string processing

Please throw in your tips and tricks to avoid memory leaks and memory related problems.

4 comments

[ 3.1 ms ] story [ 19.0 ms ] thread
A common issue for people coming from garbage-collected languages is to be very aware of what happens when you malloc() inside a function and return a pointer. Someone is going to have to free it! One common pattern is to have this malloc()ing be restricted to an obviously named foo_init() function, and then pair it with a foo_free() function that gets passed the relevant data structure, and frees anything that the _init() function malloc()ed. You then keep the _init() and _free() functions together and update them in parallel, so all the malloc()s in one are paired with a corresponding free() in the other.
"Exceptions" can be useful: lots of my code looks like

    int fd = -1;
    char *buf = NULL
    if ((fd = open("/foo", O_RDONLY, 0)) == -1)
        goto err;
    if ((buf = malloc(1024)) == NULL)
        goto err;
    ...
    return 0;
    err:
    if (fd =! -1)
        close(fd);
    free(buf);
    return -1;
Nice idea, but people would scream seeing those goto's. I used to do those in a macro/function like,

#define memalloc(x,y) { x=0;if(x=malloc(y)){}else{printf("error:no mem");} }

a simple and efficient method for finding memory leaks is replacing malloc/free with house-keeping macros when debugging:

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

  void *(*_malloc_old) (size_t size) = malloc;
  void (*_free_old) (void *ptr) = free;

  #define malloc(size) _malloc_new (size, __FUNCTION__, __LINE__)
  #define free(ptr) _free_new (ptr)

  struct alloc
  {
        const char *function;
        int line;
        void *ptr;
        struct alloc *next;
  };
  struct alloc *list = NULL;

  void *_malloc_new (size_t size, const char *function, int line)
  {
        struct alloc *e = _malloc_old (sizeof *e);
        void *ptr = _malloc_old (size);

        e->function = function;
        e->line = line;
        e->ptr = ptr;

        e->next = list;
        list = e;

        return (ptr);
  }
  void *_free_new (void *ptr)
  {
        struct alloc *prev = NULL, *e = list;
        while (e) {
                if (e->ptr == ptr) {
                        break;
                }
                prev = e;
                e = e->next;
        }
        if (e) {
                if (e == list) {
                        list = NULL;
                }
                if (prev) {
                        prev->next = e->next;
                }
                _free_old (e);
        }
        _free_old (ptr);
  }
  void pallocs (FILE *f)
  {
        struct alloc *e = list;
        while (e) {
                fprintf (f, "%s:%d\t0x%x\n", e->function, e->line, (size_t)e->ptr);
                e = e->next;
        }

  }
  int main ()
  {
        void *ptr = malloc (1024);
        void *leak = malloc (1024);
        void *leak2 = malloc (1024);

        printf ("before free:\n");
        pallocs (stdout);

        printf ("after free:\n");
        free (ptr);

        pallocs (stdout);
  }
outputs:

  before free:
  main:67 0x9dab860
  main:66 0x9dab440
  main:65 0x9dab020
  after free:
  main:67 0x9dab860
  main:66 0x9dab440