Preface: Recently a guy asked “Why Should I Ever Use C Again?” — I don’t agree with him, but at least he did say in passing that it’s okay to use C if you’re “coding on a thumb-sized computer” or bootstrapping a language. And I’ll add: Or writing device drivers. Or platform-specific kernels. But anyway …
A few years back I wrote my first web app — in C. I don’t recommend it. For web apps now I’d only use languages beginning with P, particularly P-y. But I was new to the web and to CGI, and I’d come from the world of DOS and TSRs, where using 10KB of RAM was shock-horror-huge.
Now I’m a web programmer, but only by night. By day I code firmware for embedded micros, so C is still my language of choice. By “micro” I mean processors that go in toasters and the like, with maybe 64KB of code space and 2KB of RAM. So your language choices are pretty much assembler and C. (Or Forth — but that’s another story.)
And I’ve found that the more I use C, the less I dislike it. I wanted to write up a bit of a tribute to the world’s most widespread system-level programming language.
1. K&R (love it)
Kernighan & Ritchie’s The C Programming Language is easily the best book about C, and I reckon it’s one of the best books about programming. Short, succinct, and full of useful, non-trivial examples. It’s a good read and a good reference.
Even the preface is good. A quote: “C is not a big language, and it is not well served by a big book.” All programming books would be better if they were limited to this book’s length of 270 pages. It’s quite possible the clear conciseness of K&R has a fair bit to do with C’s success.
The only other programming language book I’ve got similar fondness for is Thinking Forth by Leo Brodie. I’m sure there are other good ones — SICP, for example — it’s just that I haven’t read them yet.
2. It’s concise (love it)
The fact that it’s not a big language is a real bonus. To learn C, you only need to dig its types, flow control, pointer handling, and you’ve pretty much got it. Everything else is a function. The fact that K&R implements
qsort() in 11 lines of this low-level, imperative language is testament to its conciseness.
3. IOCCC (love it)
Call me crazy, but if you’re self-motivated, the International Obfuscated C Code Contest is probably one of the best teachers of computer science out there. And I’m only half kidding. I really think that hackers have risen to the challenge and produced some sweet didactic gems.
One entry I really learned a lot from was OTCC, Fabrice Bellard’s “Obfuscated Tiny C Compiler”. From it I learned about compiler design. Mainly that C compilers don’t have to be great hulking projects with 3.4 million lines of code. But I was also inspired to read Let’s Build a Compiler and sit down and write a mini C-to-Forth compiler.
4. Variables declared like they’re used (love it)
This one’s great for remembering how to declare more complicated things like a pointer to an array of ten integers. Is it
int *api or
int (*pai)? Well, just define it like you’d use it, and all you have to remember is operator precedence:
 binds tighter than
* (which seems to come quite naturally), so yes, you need the parens.
5. It builds a small “hello, world” (love it)
This is particularly good for embedded programming. C doesn’t have a huge run-time overhead. On many embedded processors, a do-nothing app will compile to only 3 or 4 bytes. And a “hello, world” proggy, even under Windows XP, compiles to about 1.5KB (using Tiny C Compiler, which is great for making small executables).
I think if other languages like Python could emulate this (even for a subset of the language), they could win over much of the embedded world.
6. Globals are extern by default (hate it)
“But using globals is bad practice!” you say. Not in embedded systems. Let’s say you have a file
timer.c that has a global
int counter. And in another file,
state_machine.c, you have another
counter. If you accidentally forget to make them both
static, they’re the same variable, and your code is history! No warnings, no nothing …
This seems very odd, especially given that the keyword
extern is right there handy. Once you’re familiar with the two different meanings of
static, it’s easy to avoid, but still.
7. Two different meanings for
static (hate it)
Can somebody explain to me why
static has a totally different meaning inside a function and outside of one? Inside a function it means, well, static — “keep this variable across function calls.” But outside a function it changes completely to mean “this variable’s private to this file”. Why not
intern for the latter?
8. & precedence lower than == (hate it)
For embedded programming one’s always going
if ((x&MASK) == 0), but you often forget the inner parentheses, because it seems like the precedence of
& should be higher than that of
==. But it’s not, so you’ve gotta have those extra parens.
However, there’s a good historical reason for this. C was born from B, which only had a single ANDing operator. When Ritchie introduced
&&, they wanted to keep old B-ported code working, so they left the precedence of
& lower than
9. Macros aren’t quite powerful enough (hate it)
#includes are a very neat idea, how do you do a simple preprocessor loop without resorting to brain-teasers? And, closer to something I’ve needed more than once, how do you give your program an int and string version number with only one thing to modify?
define VERSION_INT 209
define VERSION_STR "2.09"
With the above you’ve always got to change two things when you update your version number. And the special
## don’t quite do the trick. The only solutions I can figure out involve more work at runtime.
10. It’s not reflective (hate it)
Okay, so maybe this is just re-hashing point 9 — if the macro system were a bit more powerful, the language wouldn’t need to be reflective. And I may be abusing the term. But all I really mean is that with C you can’t write code that writes code.
Why didn’t they make the preprocessor language C itself? This would open up endless possibilities for unrolled loops, powerful macros, and even more IOCCC weirdness. :-)
But I think it’s great how the language fathers have no trouble admitting C’s mistakes. As Dennis Ritchie said:
“C is quirky, flawed, and an enormous success.”
Read his paper The Development of the C Language for more of this — it’s a really good read.
In short, C’s great for what it’s great for.
16 October 2007 by Ben 22 comments