Styling in C

· 5 min read

One concept that I think is not talked about enough, specifically by newer programmers, is code styling. While I don’t think this is one of the most important topics to talk about, because if the code works, it works as many people say. I do believe that in most instances it should be discussed, and I will go over some of those instances today, in the context of the C language.

Why even care?

At times you don’t need to care! But wait… didn’t I just say “in most instances it should be discussed”? Yes, I did, but the keyword is most. A lot of the time, people are on one side of the fence: that either code styles should always be enforced no matter what, or just do whatever you feel like. I believe in standing on the fence.

A lot of programmers experiment, make projects, learn, etc. alone. This is where I feel that the saying if the code works, it works fits the best. When you experiment, learn, or create something you genuinely love, worrying about code styling is like worrying about driving on a half tank. Sure, I could stop the car and fill up the tank, but that loses you time, and time is a killer for a lot of people when it comes to motivation. It also takes away from your overall focus/goal. But you don’t want to run your car to empty, right? So eventually, once you get close enough to the end or your codebase gets too hard to understand, even by your standards, this is when styling comes into play. So for the average person experimenting, trying to learn, or just creating alone, don’t worry about it too much.

If you noticed, I did preference alone. As we all know, when working in a team or open-source, the hardest thing to do is to understand others’ code. So imagine trying to understand their code while it is also in a completely different style, or even no style at all. That just adds on to an already challenging enough task. So in this instance, it’s what I would say is an essential discussion to have.

Another important distinction to make here is that style is not just about braces or indentation. It also includes things like naming conventions, line length, control flow, and how aggressively you use guard clauses or early returns. Two codebases can be formatted the same way and still feel completely different to read if their underlying style decisions are inconsistent.

Whose styling is the best?

You may be asking, “If you said I should worry about styling, then how should I style my code?” Since this is in the context of the C language, I will tell you the answer to that. The best style is the one you stick with… that doesn’t really help, does it? But it does! Because a language like C doesn’t have a universal style, unlike other languages like Python’s PEP-8. Then technically, the best style is the one that feels the best to you or your team. There is one important rule you should follow if you are to pick your own custom style, and that is to apply that said style consistently throughout your code. If you don’t do that, you don’t really have a style, do you? But you also don’t have to make your own style, there are plenty of widely accepted ones throughout the community.

This matters even more when working with others. Inconsistent styling doesn’t just look messy, it creates unnecessary noise in code reviews, makes diffs harder to read, and can even lead to avoidable merge conflicts when multiple people touch the same files. A consistent style reduces friction so discussions can focus on logic instead of formatting.

Styles are visual, so let’s have a look at some examples.

It’s also worth noting that tools like formatters and linters can help enforce a style, but they don’t define one for you. Tools are great for maintaining consistency once a style is chosen, but they work best when paired with intentional, human decisions about readability and structure.

I will now also show you my “personal” coding style that I use when I develop alone. To give you some motivation.

while (1) {
if (next_header == IPPROTO_TCP || next_header == IPPROTO_UDP ||
next_header == IPPROTO_ICMPV6) break; // Check if header is transport
if (offset + sizeof(struct ip6_ext) > size) return -1;
struct ip6_ext *exthdr = (struct ip6_ext *)(buffer + offset);
size_t exthdr_size = (exthdr->ip6e_len + 1) * 8;
if (offset + exthdr_size > size) return -1;
offset += exthdr_size;
next_header = exthdr->ip6e_nxt;
}

As you can see, I really like to utilize single-statement blocks, but only when checking for guard clauses or early returns. I feel that it allows the person reading to see what statement is related to which check or guard. It also stops the dreaded nested hell.

switch (ntohs(eth->h_proto)) {
case ETH_P_IP:
if (conf->ip_mode == IPMODE_V6_ONLY) {
return 0;
}
parse_ipv4(buffer, size, conf);
break;
case ETH_P_IPV6:
if (conf->ip_mode == IPMODE_V4_ONLY) {
return 0;
}
parse_ipv6(buffer, size, conf);
break;
case ETH_P_ARP:
if (!(conf->proto_mask & PROTO_ARP) || conf->ip_mode == IPMODE_V6_ONLY) {
return 0;
}
parse_arp(buffer, size, conf);
break;
}

Sometimes it actually does look better to use braces, like in this case. If I were to stick these checks at the top of every case, braceless, it would be easy to miss, so I use the braces to make it pop.

That is just one example of my style that I try to follow when making my own projects.

At the end of the day, code style isn’t about being correct, it’s about making your intent obvious to the next person who reads your code, including future you. Hopefully, this made you excited to try out making your own style while also teaching you the importance of it, especially in a team setting.

Have a good one, and thanks for reading.

Back to Writings