Making C Code Prettier
By Artyom Bologov
C has a bad rap for being unreadable. I've already explored the ways it can get worse, so this is a sequel complementing the original post. I'm going through the ways one can make their C code more readable and modern-looking.
Test Program
This time, I don't have a piece of code to experiment on. Most of the examples are taken from Suckless software and redacted for readability. So yes, this post is a bit of appreciation for the community work. Now, to the exact prettiness hacks.
Good Code Style
I know I listed indentation styles as ugly in the previous post. But that's the whole point of indentation styles: making the code more readable. Some styles look ugly but accomplish a certain goal nonetheless.
I'm using a Linux Kernel Style in my projects, because it looks good and promotes good practices. But you can always call me out as wrong.
Non-Bracketed Blocks (pre-ANSI)
This one is pretty obvious: you can omit the curly braces marking block start/end. In most control structures: if/else, for, do/while. Suckless software utilizes that for increased readability and brevity.
Looks quite Pythonic to me. Do we need Python if we have this in C?
More Readable Types and Constants (C99)
Missing true/false when dealing with boolean data?
Not sure how may bytes a certain value occupies?
Like how Rust did with u32
and i64
?
C99 headers—
stdbool.h
for booleans,
stdint.h
for fixed width types—to the rescue!
There are more types in this snippet—ushort, Rune, Glyph.
But these are Suckless types.
Still, sane typedefs
make your code more readable.
Well, if you're considerate enough.
Compound (C99) and Anonymous (C11) Initializers/Literals
Python and JavaScript have a readable literal object syntax:
C has more primitive data structures, but there are literal initializers that share some of this readability.
Here's initializer-based Surf config snippet (actually part of my setup) that shows why Suckless software is so readable and configurable:
There is also an option (since C11) to pass anonymous structures to functions, instead of allocating, passing, freeing the data just for one call. Saves you a couple of lines and many use-after-free errors.
Generic Dispatch (C11) and Type Inference (C23)
C is statically typed and you have to provide types for everything. And it's overly verbose and uncomfortable to write programs in. Right? There actually is generic dispatch in C11
And there is type inference starting from C23:
That's a useful example for
typeof
operator:
inferring some macro argument type.
But there's a more useful and immediate side to it,
like Go's
:=
and
C#
var
: auto
.
Conclusion
While this is nowhere close to the full listing of quality of life features, I'll stop here. The main point is delivered: C might be quite readable, and there's a whole software ecosystem exploiting it—Suckless. In case you have an impression that C is undecypherable, you might enjoy checking out modern C. Especially with the awesome changes introduced with C23!