MAKING C CODE PRETTIER (#📎) 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 (#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 (#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) (#non-bracketed-blocks) 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. =================================== c =================================== void tdefutf8(char ascii) { if (ascii == 'G') term.mode |= MODE_UTF8; else if (ascii == '@') term.mode &= ~MODE_UTF8; } ==================== A function from st using non-bracketed blocks ==================== Looks quite Pythonic to me. Do we need Python if we have this in C? MORE READABLE TYPES AND CONSTANTS (C99) (#types-and-constants) 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! =================================== c =================================== typedef struct { Rune u; ushort mode; uint32_t fg; uint32_t bg; } Glyph; ==================== Snippet with fixed-width types from st.h ==================== 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 (#compound-anonymous-initializers-literals) Python and JavaScript have a readable literal object syntax: =================================== json =================================== {key1: value1, key2: value2} [elem1, elem2, elem3] ==================== JSON-like Python/JavaScript initialization syntax ==================== C has more primitive data structures, but there are literal initializers that share some of this readability. =================================== c =================================== // Structure initializers: {value1, value2} {.key1 = value1, .key2 = value2} // Array initializers: {elem1, elem2, elem3} {[0] = elem1, [1] = elem2, [2] = elem3} ==================== C99 compound initializers ==================== Here's initializer-based Surf config snippet (actually part of my setup) that shows why Suckless software is so readable and configurable: =================================== c =================================== 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 }, }, }; ==================== Surf config snippet with compound array initializer (redacted for readability) ==================== 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) (#"generic-dispatch-type-inference") 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 =================================== c =================================== #define print_val (val) _Generic((val), int: printf("%in", val), char*: puts(val)) ==================== Example of generic dispatch from C11 ==================== And there is type inference starting from C23: =================================== c =================================== #define iter (times) for(typeof(times) i = 0; i < times; ++i) ==================== Use of C23 typeof in macros ==================== 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'. =================================== c =================================== auto i = 3; ==================== C23 type-inferring keyword ==================== CONCLUSION (#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! CC-BY 4.0 2022-2026 by Artyom Bologov (aartaka,) with one commit remixing Claude-generated code . Any and all opinions listed here are my own and not representative of my employers; future, past and present.