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.
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.
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.
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.
void
tdefutf8(char ascii)
{
if (ascii == 'G')
term.mode |= MODE_UTF8;
else if (ascii == '@')
term.mode &= ~MODE_UTF8;
}
Looks quite Pythonic to me. Do we need Python if we have this in C?
Missing true/false when dealing with boolean data?
Not sure how may bytes a certain value occupies?
Like how Rust did with
There are more types in this snippet—ushort, Rune, Glyph.
But these are Suckless types.
Still, sane
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.
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
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!
typedef struct {
Rune u;
ushort mode;
uint32_t fg;
uint32_t bg;
} Glyph;
Compound (C99) and Anonymous (C11) Initializers / Literals
{key1: value1, key2: value2}
[elem1, elem2, elem3]
// Structure initializers:
{value1, value2}
{.key1 = value1, .key2 = value2}
// Array initializers:
{elem1, elem2, elem3}
{[0] = elem1, [1] = elem2, [2] = elem3}
static Parameter defconfig[ParameterLast] = {
/* parameter Arg value priority */
[AccessMicrophone] = { { .i = 0 }, },
[AccessWebcam] = { { .i = 0 }, },
[Certificate] = { { .i = 0 }, },
[CaretBrowsing] = { { .i = 1 }, },
[CookiePolicies] = { { .v = "@" }, },
[DarkMode] = { { .i = 1 }, },
[DefaultCharset] = { { .v = "UTF-8" }, },
[DiskCache] = { { .i = 1 }, },
[DNSPrefetch] = { { .i = 0 }, },
/* ... */
[MediaManualPlay] = { { .i = 1 }, },
[PreferredLanguages] = { { .v = (char *[]){ "en_US" } }, },
/* ... */
[WebGL] = { { .i = 0 }, },
[ZoomLevel] = { { .f = 1.3 }, },
};
Generic Dispatch (C11) and Type Inference (C23)
#define print_val (val) _Generic((val), int: printf("%in", val), char*: puts(val))
#define iter (times) for(typeof(times) i = 0; i < times; ++i)
auto i = 3;
Conclusion