Suppose I write my code very defensively and always check the return types from all the functions that I call.
So I go like:
char* function() {
char* mem = get_memory(100); // first allocation
if (!mem) return NULL;
struct binder* b = get_binder('regular binder'); // second allocation
if (!b) {
free(mem);
return NULL;
}
struct file* f = mk_file(); // third allocation
if (!f) {
free(mem);
free_binder(b);
return NULL;
}
// ...
}
Notice how quickly free()
things get out of control. If some of the function fails, I have to free every single allocation before. The code quickly becomes ugly and all I do is copy paste everything over. I become a copy/paste programmer, even worse, if someone adds a statement somewhere in between, he has to modify all the code below to call the free()
for his addition.
How do experienced C programmers go about this problem? I can't figure anything out.
Thanks, Boda Cydo.
I know I'll get lynched for this, but I had a friend who said they used
goto
for that.Then he told me that it wasn't enough in most cases and he now used
setjmp()
/longjmp()
. Basically he reinvented C++'s exceptions but with much less elegance.That said, since
goto
could work, you could refactor it into something that doesn't usegoto
, but the indentation will get out of hand fast:BTW, scattering the local variable declarations around the block like that isn't standard C.
Now, if you realize that
free(NULL);
is defined by the C standard as a do-nothing, you can simplify the nesting some:If you want to do it without
goto
, here is an approach that scales well:You could define a new label that would free the resources and then you could GOTO it whenever your code fails.
Error management with GOTO was already discussed here: Valid use of goto for error management in C?
You could also take the opposite approach and check for success:
If your data structures are complicated/nested, a single goto might not suffice, in which case I suggest something like:
A real world example would be more complicated and could involve multiple levels of structure nesting, linked lists, etc. Note here that
free(mystruct->data);
cannot be called (because dereferencing an element ofmystruct
is invalid) if the firstmalloc
failed.While I admire your approach to defensive coding and that's a good thing. And every C Programmer should have that mentality, it can apply to other languages as well...
I have to say this is the one thing useful about GOTO, despite purists will say otherwise, that would be an equivalent of a finally block but there's one particular gotcha that I can see there...
karlphillip's code is nearly complete but .... suppose the function was done like this
Be careful!!! This would depend on the design and the purpose of the function that you see fit, that put aside.. if you were to return from the function like that, you could end up falling through the
release_resources
label.... which could induce a subtle bug, all the references to the pointers on the heap are gone and could end up returning garbage... so make sure if you have the memory allocated and return it back, use thereturn
keyword immediately before the label otherwise the memory could disappear... or create a memory leak....