Author: Casper Beyer
The front cover of the “C Programming Language” book as I recall it from memory
Why on earth would someone would pick C to start a new project in 2020? Surely there is a newer language with more shiny features that’s better right? Well I can’t speak for other people but I’ll tell you my reasons.
First of all let me preface this by saying that of course this is a biased opinion and the language I pick for something depends on the context it’s going to be used in. For example; I doubt I’ll ever be reaching for C when writing a web service simply because the ecosystem around that domain isn’t great and I’m not itching to write my http framework at this time.
But for games, more specifically cross-platform games C is a clear winner for me because it provides me with exactly the things I’m looking for which is reliability, simplicity and performance.
This is the most important and only real non-negotiable requirement for me; the reliability and portability of the toolchain and the access it provides to system and device interfaces.
Languages, especially dynamic ones and their runtime environments come and go like fashion. When your target platform suddenly gets deprecated from the runtime you’re relying on, well then you’re just fucked and out of luck.
I’ve been in this situation before with multiple languages and I don’t intent to repeat history here so I need something stable, something will have a reliable toolchain available on all the platforms I could ever possibly want in perpetuity without having to worry about compiler bugs as I’m more than proficient in the art of introducing my own bugs.
The ecosystem around C provides all of this. Even for the most obscure platform you can imagine there will be solid stable toolchain with a good set of native libraries that provide access to system and device interfaces for said platform.
While I will happily write software in slower languages without that much complaint, performance is fairly high up on the list when it comes to writing games.
Avoiding cache misses is key to that, so having any kind of dynamic language with an interpreter is hopeless. Even in the best case scenario that the platform of choice provides you with one of the magical JIT compilers available today, they’re still magical black boxes which makes it difficult to reason about performance characteristics object boxing/unboxing and cache locality.
Same goes for stop the world garbage collection, again there are some fairly impressive implementations out there; Go’s garbage collector comes to mind but it’s still a garbage collector and it will still lead to frame drops. If you have to use object pools to tip-toe around garbage collector killing the frame-rate then what’s the point of having one in the first place.
Even for a low fidelity game filled with chunky pixelated goodness having more cycles gives you a lot of room to experiment and still have plenty of room to add copious amounts of extra juice.
But It’s not just about running fast, battery life matters as-well. Fewer cycles means less battery consumption which is a major win if you ask me. As long as I can help it, I’ll do my best to avoid draining a user’s battery in five minutes.
Lastly, when it comes to the web and mobile the executable size also has to be considered. While it’s not a deal breaker it’s a nice bonus that if you work on it a little bit C executables built with WebAssembly essentially have zero bytes of overhead by default. It might seem like a minor thing but it’s the difference between loading instantly and taking 3–5 minutes to load on a connection.
While it’s not a critical requirement, a nice thing about C is that it is an extremely simple language which is a nice welcome break from working in monolithic languages that get jam packed with new features every time someone on the design team or committee learns about a new feature in another language causing a paradigm shift until the next iteration which causes another paradigm shift leaving the whole ecosystem in a bit of a tangled mess.
When I’m saying C simple I don’t necessarily mean easy. If you don’t know what you’re doing C will absolutely blow up in your face and make you spend the day trying to figure out what you did wrong. But again it’s a simple language so it’s really not too hard learning how to write well-behaved programs. Secure programs is a different matter but well-behaved programs are easy-enough.
Now there’s definitively features that would be nice to have in C; I wouldn’t hate having quality of life things like arrays that don’t decay to naked pointers, modules or deferred statements for example but not having them aren’t total deal breakers.
At the end of the day I prefer having a limited surface area in the language than having a language that gets in the way bringing absurd amounts of complexity and bad abstractions to the problem.
This applies to any language in theory but certain languages encourage you a-lot more to be ‘clever’ more than others. I prefer to be pragmatic because if one is trying to be clever when writing a program, then good luck because one needs to be twice as clever debugging and maintaining it in the long run.