There are 2 different forms of this argument:
- Our world (the free software world) uses C for everything from editors and shells to mail clients and all other manner of GUI programs. I want to convince people that they should not write such software in C.
- While C is currently a good choice for low-level software such as operating systems and device drivers, I want to convince people that we do need to replace C even for those uses.
Many of my arguments against C apply to both cases, hence why I treat them together here.
C is weak
Many on the internet resist the very concept of vertical comparisons between languages. They insist choosing a language is always a matter of taste or use case. But there is a vertical dimension here. Paul Graham's essay Beating the Averages has a great explanation of this point, under "The Blub Paradox", but in summary: the relationship between a high-level language and C is of the same kind as the relationship between C and assembly.
Beating the Averages
There are even other low-level (ie. manual memory mamagement) languages that are much more powerful than C while being nearly as *simple*, such as Zig and Hare (albeit those aren't really production ready yet, I have a lot of hope for them).
C is unreadable
Something that isn't brought up enough is that C has unreadability built into the very standard in a way that no other language does. C stdlib functions have spectacularly inscrutable nomenclature; some highlights:
- `a64l` - convert a base64 ASCII string to a long. In any other language, this would be named something like "base64_to_long".
- `wcstol` - "wide char string to long". Note the inconsistent spelling from `strtol` - "string to long".
- `rint` - "round to int" (takes and returns a double). God, why couldn't it just be called *round*?
The harm of such nomenclature is well understood by any experienced programmer.
C is hard to build
C and C++ projects are absurdly difficult to compile. There are many different "build systems" in common use so there's no standard way to compile a project, and even on a mainstream Linux distribution, you almost always have to debug a series of platform-dependent errors about not being able to find libraries or header files or the wrong build tool flags or even just compile errors in the source code that, presumably, don't show up for the developers and you don't know why. A story I always share when this topic comes up is of my GTK patch that fixed a GObject Introspection annotation: I spent *12 hours* trying to get GTK to compile, until I gave up and submitted the patch without having seen a successful build (it got merged). That should be terrifying.
Build systems are a scourge
Because the C language has no real concept of a dependency or import, this is a problem with the language itself, not just the ecosystem.
In almost any modern language, like Rust or Go, you run `cargo build` or `go build` and that's *it*. I have never had any significant difficulty building a project in those languages (except where C libraries were involved).
C puts bugs in the lap of the user
This one has been beaten to death by Rust advocates, but I inculde it here just for completeness. Because of not only C's propensity for memory safety bugs, but also the approach it takes to handling errors (silently ignore unless explicitly handled, and also where errors can happen is implicit) it is much more likely that bugs end up in the lap of the user rather than the developer (and thus, ultimately back in the lap of the developer but harder to debug).
The C spec is proprietary
The C spec documents are copyrighted by ISO. Yes, the practical importance is very low because there's lots of third-party documentation about C and the implementations are free, but it is simply offensive.
What inspired me to mention this was the Hyperbola devs making a similar complaint about Rust (basically, Rust is trademarked in a way that prohibits forking it without rebranding it):
And they're spot on, that's offensive too and a good reason to not use Rust. But also like the C issue, its practical importance is very low (how likely is it that anyone would *seriously* fork Rust?), so I point this out because I think that if you (like me) consider it a good reason to not use Rust, you should also consider it a good reason to not use C.
Arguments for C
It would only be fair to consider the reasons to use C as well.
C still has the widest range of supported targets. Every other language has platforms it doesn't support (or doesn't support well), and most of them have omissions that are very important to me like Plan 9.
However. From the perspective of "what language should I write this project in", for many projects this doesn't actually matter. Most end-user applications, for example, don't support more than a couple platforms anyway because they depend on platform-specific interfaces like window systems.
And as for Plan 9: the entire system is so different that any significant C program is not likely to work without porting or emulation anyway.
Zig, though early in development, is so far being conscientous about support for non-mainstream platforms, and I think it will eventually achieve this level of portability.
Drew DeVault: Abiopause
The argument is roughly "any program that's going to interface with code from other languages must speak the C ABI, and providing such an ABI from a high-level language is a cost - you must write extra, unidiomatic code".
This is a valid issue, but is mostly relevant for libraries, not applications. Application code is not going to be used from another language. And with languages like Zig, the Abiopause is pretty thin. I wrote a Wayland client in Zig using libwayland-client directly and it wasn't that bad.
Other compiled languages are fast, and for most things, the difference between a fast language and a very fast language is just not a priority. After all, it probably could be even faster if you wrote it in assembly, but I don't see many people doing that, even for performance-critical things like OS kernels.
And about performance: Zig may not be there yet, but I'm pretty sure it'll be actually faster than C, since it's similarly low-level and facilitates more optimizations.