Comment

Comment by asveikau

This seems like a pretty good list. Focus on error handling is spot-on, most good C is just simply that.

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);
parent asveikau 11 years ago

Discussion

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton