One of my favorite tricks is to make your success case and your error cases look largely the same.
Say you have N allocations and N things that can fail. Lots of people get into trouble when they do:
void *p, *q, *r, *s;
p = a();
if (!p) { return ERROR; }
q = b();
if (!q) { free(p); return ERROR; }
r = c();
if (!r) { free(p); free(q); return ERROR; }
// etc..
And this madness just goes on and on. Add a new allocation? You potentially have to change n cleanup blocks.I breathe a lot easier when I can do [note we abuse the fact that free(NULL) is a no-op]:
void *p = NULL;
void *q = NULL;
void *r = NULL;
p = a();
if (!p) goto cleanup;
q = b();
if (!q) goto cleanup;
r = c();
if (!r) goto cleanup;
cleanup:
free(p);
free(q);
free(r);
What if the buffer at "r" needs to return something to the caller? It turns out it's really easy to transfer ownership in this pattern: // Success case. Give `r` to the caller.
*callerPointer = r;
r = NULL;
// Then we fall through to the failure block...
// r is null now so free(r) is harmless.
cleanup:
free(p);
free(q);
free(r);