Smart pointers/safe memory management for C?

CMemoryPointersC99Smart Pointers

C Problem Overview


I, and I think many others, have had great success using smart pointers to wrap up unsafe memory operations in C++, using things like RAII, et cetera. However, wrapping memory management is easier to implement when you have destructors, classes, operator overloading, et cetera.

For someone writing in raw C99, where could you point (no pun intended) to help with safe memory management?

Thanks.

C Solutions


Solution 1 - C

The question is a bit old, but I figured I would take the time to link to my smart pointer library for GNU compilers (GCC, Clang, ICC, MinGW, ...).

This implementation relies on the cleanup variable attribute, a GNU extension, to automatically free the memory when going out of scope, and as such, is not ISO C99, but C99 with GNU extensions.

Example:

simple1.c:

#include <stdio.h>
#include <csptr/smart_ptr.h>

int main(void) {
    smart int *some_int = unique_ptr(int, 1);

    printf("%p = %d\n", some_int, *some_int);

    // some_int is destroyed here
    return 0;
}

Compilation & Valgrind session:

$ gcc -std=gnu99 -o simple1 simple1.c -lcsptr
$ valgrind ./simple1
==3407== Memcheck, a memory error detector
==3407== Copyright (C) 2002-2013, and GNU GPL\'d, by Julian Seward et al.
==3407== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==3407== Command: ./simple1 
==3407==
0x53db068 = 1
==3407==
==3407== HEAP SUMMARY:
==3407==     in use at exit: 0 bytes in 0 blocks
==3407==   total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==3407==
==3407== All heap blocks were freed -- no leaks are possible
==3407==
==3407== For counts of detected and suppressed errors, rerun with: -v
==3407== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Solution 2 - C

It's difficult to handle smart pointers in raw C, since you don't have the language syntax to back up the usage. Most of the attempts I've seen don't really work, since you don't have the advantages of destructors running when objects leave scope, which is really what makes smart pointers work.

If you're really worried about this, you might want to consider just directly using a garbage collector, and bypassing the smart pointer requirement altogether.

Solution 3 - C

Another approach that you might want to consider is the pooled memory approach that Apache uses. This works exceptionally well if you have dynamic memory usage that is associated with a request or other short-lived object. You can create a pool in your request structure and make sure that you always allocate memory from the pool and then free the pool when you are done processing the request. It doesn't sound nearly as powerful as it is once you have used it a little. It is almost as nice as RAII.

Solution 4 - C

You cannot do smart pointers in C because it does not provide necessary syntax, but you can avoid leaks with practice. Write the resource release code immediately after you allocated it. So whenever you write a malloc, you should write the corresponding free immediately in a cleanup section.

In C I see the 'GOTO cleanup' pattern a lot:

int foo()
{
    int *resource = malloc(1000);
    int retVal = 0;
    //...
    if (time_to_exit())
    {
        retVal = 123;
        goto cleanup;
    }
cleanup:
    free(resource);
    return retVal;
}

In C we also use a lot of contexts which allocate stuff, the same rule can be applied for that too:

int initializeStuff(Stuff *stuff)
{
    stuff->resource = malloc(sizeof(Resource));
    if (!stuff->resource) 
    {
        return -1; ///< Fail.
    }
    return 0; ///< Success.
}

void cleanupStuff(Stuff *stuff)
{
    free(stuff->resource);
}

This is analogous to the object constructors and destructors. As long as you don't give away the allocated resources to other objects it won't leak and pointers won't dangle.

It's not difficult to write a custom allocator that tracks allocations and writes leaking blocks atexit.

If you need to give away pointers to the allocated resources you can create wrapper contexts for it and each object owns a wrapper context instead of the resource. These wrappers share the resource and a counter object, which tracks the usage and frees the objects when no one uses it. This is how C++11's shared_ptr and weak_ptr works. It's written in more detail here: https://stackoverflow.com/questions/5671241/how-does-weak-ptr-work

Solution 5 - C

Static code analysis tools like splint or Gimpel PC-Lint may help here -- you can even make these moderately "preventative" by wiring them into your automatic "continuous-integration" style build server. (You do have one of those, right? :grin:)

There are other (some more expensive) variants on this theme too...

Solution 6 - C

You can define macros, for example BEGIN and END, to be used in place of braces and trigger automatic destruction of resources that are exiting their scope. This requires that all such resources are pointed to by smart pointers that also contain pointer to the destructor of the object. In my implementation I keep a stack of smart pointers in heap memory, memorize the stack pointer at entry to a scope, and call destructors of all resources above the memorized stack pointer at scope exit (END or macro replacement for return). This works nicely even if setjmp/longjmp exception mechanism is used, and cleans up all the intermediate scopes between the catch-block and the scope where the exception was thrown, too. See https://github.com/psevon/exceptions-and-raii-in-c.git for the implementation.

Solution 7 - C

If you are coding in Win32 you might be able to use structured exception handling to accomplish something similar. You could do something like this:

foo() {
    myType pFoo = 0;
    __try
    {
        pFoo = malloc(sizeof myType);
        // do some stuff
    }
    __finally
    {
        free pFoo;
    }
}

While not quite as easy as RAII, you can collect all of your cleanup code in one place and guarantee that it is executed.

Solution 8 - C

Ok, so here are your options. Ideally, you combine them to get better result. In case of C, paranoia is fine.

Compile-time:

  1. Use the cleanup variable attribute in GCC. You have to stick to GCC after that. This limits portability of your code because you can only target platforms for which GCC exists.
  2. Use SEH (structured exception handling) on Windows. This limits your portability even further because your have to use the Microsoft compiler. If your target is exclusively Windows, then it will work for you.
  3. Use the static code analysis tool revealing potential memory leaks. Doesn't work perfectly, but can help find trivial leaks. Doesn't affect your portability.

Runtime:

  1. Use the debug memory allocation library which replaces malloc/free with its own implementations and tracks memory usage. This way you can see blocks that were allocated but never released. I had success with the one for Solaris (will try to remember its name).
  2. Use a garbage collector. I had a positive experience with Hans-Boehm GC while fixing a very leaky C application that I didn't have the source code for. I could see how memory consumption was climbing, then it plummeted when the GC did its work.

Solution 9 - C

Sometimes i use this approach and it seems good :)

Object *construct(type arg, ...){

    Object *__local = malloc(sizeof(Object));
    if(!__local)
        return NULL;
    __local->prop_a = arg;
    /* blah blah */
    

} // constructor

void destruct(Object *__this){

   if(__this->prop_a)free(this->prop_a);
   if(__this->prop_b)free(this->prop_b);
   
} // destructor

Object *o = __construct(200);
if(o != NULL)
   ;;

// use

destruct(o);

/*
  done !
*/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmannickenView Question on Stackoverflow
Solution 1 - CSnaipeView Answer on Stackoverflow
Solution 2 - CReed CopseyView Answer on Stackoverflow
Solution 3 - CD.ShawleyView Answer on Stackoverflow
Solution 4 - CCalmariusView Answer on Stackoverflow
Solution 5 - CleanderView Answer on Stackoverflow
Solution 6 - Cuser3510229View Answer on Stackoverflow
Solution 7 - CSteve RoweView Answer on Stackoverflow
Solution 8 - CraiksView Answer on Stackoverflow
Solution 9 - CTigerView Answer on Stackoverflow