Book Review: 21st Century C

I recently got back into C programming as part of my day job, and I decided to pick up a few books to refresh my memory. 21st Century C by Ben Klemens wasn’t quite what I was looking for, but it caught my eye due to the unusual title, if nothing else. People assume that C is an ossified language from an earlier time, but the author’s bold contention is that much has changed in the last decade or so (primarily with the 1999 ISO standard update) and that there is plenty new to learn. It’s an intriguing idea, but does it live up to reality?

Klemens is certainly right that most people haven’t kept up to date with the changes to the C standards. However, there’s a very good reason for that: most people who are using C professionally are doing it in environments where there isn’t a choice. The only new projects for which C is an appealing choice are cases where the code needs to compile on legacy platforms. Perhaps some projects that are using C for historical reasons could update, but there’s usually a lot of resistance to changing idioms while maintaining legacy code. Perhaps more awareness of what people are missing out on could swing more projects in favour of using C99 (or even the updated 2011 standard), but I’m not holding my breath.

So if we put aside the idea that C is a much-improved language due for a renaissance, how does the book stack up? Overall I’d say not that bad, although it doesn’t seem all that clear on who its target audience is. People who use C on a daily basis will find something of value in the big picture, but too much time is spent re-treading familiar ground. People who don’t know C but want to learn (if there are any) will find it lacking in introductory material. Though it covers new standards, it’s definitely not a book for language lawyers. Perhaps it will work best for someone like me, who used to use C but haven’t picked it up for a while, although even there the usefulness is patchy.

The book is framed using a device that is all too common in technical books, and that I hate: the author takes a preferred hobby of theirs (in this case, punk rock) and proceeds to apply aspects of it to the material presented (often just as a way of setting the scene for chapters, but sometimes as direct analogies to technical concepts). This rarely works all that well. For a start, authors tend to choose things that they care about (and that they want the world to know they care about) rather than things that make for good exposition. Secondly, it tends to strike a discordant note with the rest of the content of the book. A good technical book can have personality, but rarely is it good because of the personality it presents. In this case the references to music don’t add anything of value.

The first half of the book is spent on material that has nothing to do with C as such: how to install libraries, how to use version control, and how to package code using Autotools. This works better than you might expect, but a lot of it feels like filler. Once again, the author seems vague about his target audience: some attention is paid to options for compiling code on Windows, but not enough for anyone to fully adapt the techniques shown. It would be better to abandon Windows and declare this a UNIX book, or to give comprehensive guidance for Windows best practices.

The second half settles down into the material where the book has something unique to offer: best practices for writing C, with particular reference to the 1999 and 2011 revisions to the standard. Even this part of the book I found to be patchy. There are some sensible idioms making use of variadic macros, anonymous structs and the _Generic operator that have some potential. On the other hand, the author’s attempts to make C look like Python seem to be missing the point, and his descriptions betray confusion on one or two technical points.

As one example, the author provides the following code for freeing several pointers in one line:

#define Fn_apply(type, fn, ...) {                                      \
    void *stopper_for_apply = (int[]){0};                              \  
    type **list_for_apply = (type*[]){__VA_ARGS__, stopper_for_apply}; \
    for (int i=0; list_for_apply[i] != stopper_for_apply; i++)         \
         fn(list_for_apply[i]);                                        \
}
 
#define Free_all(...) Fn_apply(void, free, __VA_ARGS__);
 
int main(){
    double *x= malloc(10);
    double *y= malloc(100);
    double *z= malloc(1000);
 
    Free_all(x, y, z);
}

The only advantage this gives is that you save yourself a couple of lines when you have a list of pointers to be freed all in one place. However, it has a significant drawback: NULL is used to terminate the variable-length list, so if any of the pointers in the list is NULL you will leak memory. This is an important change to the semantics of free() (which is deliberately defined such that free(NULL) is a no-op) and turns a safe function into a dangerous one. This kind of C-Python hybrid is cute but doesn’t solve any of the problems of C in the real world.

Edit: The paragraph above is completely wrong, as Ben points out in the comments. Mea culpa.

Probably the best way to look at a book like this is to treat it like Alexandrescu’s Modern C++ Design (which is admittedly a far better book): as a source of inspiration and ideas, without applying any of the techniques directly. It’s great to keep abreast of the changes in the world of C, even if few people can use that knowledge just now. And although this book has its weak points and a lot of padding, it might be the only game in town.

2 thoughts on “Book Review: 21st Century C

  1. bk

    Tim,

    Thank you for your comments on my book. It seems you got a few things out of it, even if it didn’t move you overall.

    Let me point out one factual point about the code sample. The macro declares
    void *stopper_for_apply = (int[]){0};
    This is a pointer to a list of one integer (which happens to be zero). We don’t know where that list will be in memory, but it is a specific point that is certainly not NULL. That stopper is appended to the end of the list given by the user, and the for loop steps through the list until it hits that stopper. NULL values do not match stopper_for_apply, so the for loop handles them like any other pointer.

    Regards,

    BK

    Reply
    1. admin Post author

      You’re totally right. I read that as a cast of zero, but obviously I was being hasty. Maybe I should have read the book more closely. 🙂 I’ll amend the article accordingly.

      Thanks for stopping by my blog and taking the time to put me right, Ben.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *