Lessons-to-learn-from-C.md (4474B)
- ---
- date: 2017-01-30
- # vim: tw=80
- title: Lessons to learn from C
- layout: post
- tags: [C, language design]
- ---
- C is my favorite language, though I acknowledge that it has its warts. I've
- tried looking at languages people hope will replace C (Rust, Go, etc), and
- though they've improved on some things they won't be supplanting C in my life
- any time soon. I'll share with you what makes C a great language to me. Take
- some of these things as inspiration for the next C replacement you write.
- First of all, it's important to note that I'm talking about the language, not
- its standard library. The C standard library isn't *awful*, but it certainly
- leaves a lot to be desired. I also want to place a few limitations on the kind
- of C we're talking about - you can write bad code in any language, and C is no
- different. For the purpose of argument, let's assume the following:
- - C99 minimum
- - Absolutely no code in headers - just type definitions and function prototypes
- - Minimal use of typedefs
- - No macros
- - No compiler extensions
- I hold myself to these guidelines when writing C, and it is from this basis that
- I compare other languages with C. It's not useful to compare bad C to another
- language, because I wouldn't want to write bad C either.
- Much of what I like about C boils down to this: **C is simple**. The ultimate
- goal of any system should be to attain the simplest solution for the problems it
- faces. C prefers to be conservative with new features. The lifetime of a feature
- in Rust, for example, from proposal to shipping is generally 0 to 6 months. The
- same process in C can take up to 10 years. C is a venerable language, and has
- already long since finished adding core features. It is stable, simple, and
- reliable.
- To this end, language features map closely to behaviors common to most CPUs. C
- strikes a nearly perfect balance of usability versus simplicity, which
- results in a small set of features that are easy to reason about. A C expert
- could roughly predict the assembly code produced by their compiler (assuming
- `-O0`) for any given C function. It follows that C compilers are easy to write
- and reason about.
- The same person would also be able to give you a rough idea of the
- performance characteristics of that function, pointing out things like cache
- misses and memory accesses that are draining on speed, or giving you a precise
- understanding of how the function handles memory. If I look at a function in
- other languages, it's much more difficult to discern these things with any
- degree of precision without actually compiling the code and looking at the
- output.
- The compiler also integrates very comfortably with the other tools near it, like
- the assembler and linker. Symbols in C map 1:1 to symbols in the object files,
- which means linking objects together is simple and easily reasoned about. It
- also makes interop with other languages and tools straightforward - there's a
- reason every language has a means of writing C bindings, but not generally C++
- bindings. The use of headers to declare external symbols and types is also nicer
- than some would have you believe, since it gives you an opportunity to organize
- and document your API.
- C is also the most portable programming language in the world. Every operating
- system on every architecture has a C compiler, and they weren't really
- considered a viable platform until it did. Once you have a C compiler you
- generally have everything else, because everything else was either written in C
- or was written in a language that was implemented in C. I can write C programs
- on/for Linux, Windows, BSD, Minix, plan9, and a dozen other niche operating
- systems, or even no operating system, on pretty much any CPU architecture I
- want. No other language supports nearly as many platforms as C does.
- With these benefits acknowledged, there are some things C could do better. The
- standard library is one of them, but we can talk about that some other time.
- Another is generics; using void* all the time isn't good. Some features from
- other languages would be nice - I would take something similar to Rust's match
- keyword. Of course, the fragility of memory management in C is a concern that
- other languages are wise to address. Undefined behavior is awful.
- Even for all of these warts, however, the basic simplicity and elegance of C
- keeps me there. I would love to see a language that fixes these problems without
- trying to be the kitchen sink, too.
- In short, I like C because **C is simple**.