I've been learning Scheme lately, on and off, an evening here and there, when I take a bit of time off of my immediate development requirements. Scheme is a very elegant language, which I admire for its beautiful syntax. Here's an RSA decryption program in Scheme:
(write (number->string
(let
((msg (string->number "0FB78CC9...283E9F69...9AA34BC1" 16))
(mod 4847438932...5371616465...9691290563))
(remainder (expt msg 17) mod))
16))
Simple, eh? The variable 'msg' receives a big fat RSA ciphertext in hexadecimal, the variable 'mod' receives the modulus of the RSA public key, and the public exponent (17) is just keyed in. The code computes (msg**17 % mod) and prints the result as a hexadecimal string. It executes in a blink using the Petite Chez Scheme interpreter, which Cadence Research Systems kindly make available for free. (They make money on the compiler, which I hope one day I'll have a good reason to purchase. I'd like to contribute to continued development of neat stuff like this.)

So, a few minutes ago I was looking at the straightforward implementation of a simple round-robin system in Scheme, trying to understand how it might actually be executed efficiently, and then it dawns on me - the reason why I first started programming in C.

Programming in C is great because the language makes it easy for you to understand what the machine will execute.

Programming in C is rewarding because it's like this neat, little, clunky macro language on top of assembler, and you can easily visualize how each of your statements and expressions will be translated into machine code. You know exactly what each line of your code will do. It's never fuzzy. There's never anything behind the scenes, the compiler doesn't add any magical dust or go through any mystical motions to make things easier for you. The code executes exactly as you write it.

This is important, because understanding what the machine does is crucial in a performance-sensitive application, which all professionally written programs are. When you encounter a performance problem, the straightforward relationship between C and machine code makes it easier to analyze a program and find bottlenecks. It makes it easier to not write bottlenecks in the first place.

On the other hand, in a language that does things behind the scenes to make things easier for you, you not only have to understand your code, you also have to understand the things that are supposed to be hidden from you. Because those things get executed, too. If you don't have an understanding of how the language transforms the program into machine code, you cannot understand why it executes 1000 times slower than you need it to.

And that's the appeal of C, as well as C++. These are languages that aren't there. C++ is, effectively, a big but conceptually simple macro language on top of the machine code. It is a design goal of these languages to keep the background infrastructure minimal. Unless you count the run-time library, there's very little that happens behind the scenes that you don't know.