Home > Uncategorized > Three Things I Love About C

Three Things I Love About C

Like many other coders out there, I am multilingual.

Well, I’m not really multilingual per se… I can speak English passably and I know enough Spanish to make a fool of myself. Obviously, I’m talking about programming languages.

In no particular order, I have (at one time or another) dabbled in Scheme, Pascal, QBasic, VB.net, C#, Prolog, Python, PHP, pl/SQL, Perl, Ruby, x86 Assembly, machine code, bash/shell, Java, GLSL, and C++, and C. And probably some others I’ve forgotten.

I like Ruby and Python a good deal but don’t spend much time using them, most of the code I write goes into filesystems or drivers – so not suprisingly, I’m a big fan of C.

Here are three of my favorite things about C, things that absolutely tickle me pink and make me love coding in C.

Inline Assembly

What other language lets you do this:

asm("int $3");

Or better yet,

asm("leal -4(%esp), %ecx");

Inline assembly into C programs gives incredible flexibility and power to the system designer. Of course, in the wrong hands, inline assembly can hose up the system and make software suck. But you can’t deny the raw awesome computing power afforded by being able to inject assembly directly.

The Preprocessor

From macros to textual replacement to conditional compilation, the C preprocessor let’s you do some neat things which I find both frustrating and amazing.

A few stupid examples:

#ifdef __KERNEL__
#define READ_REGISTER(addr) ioread32(addr)
#define READ_REGISTER(addr) call_other_func(addr)

This can be used to support multiple OSes and/or environments without incurring runtime penalties in a common, shared codebase.

#define MAGIC_NUMBER 5
#ifdef __KERNEL__
#define printf(...)   printk(KERN_ERR, __VA_ARGS__)
printf("%d", MAGIC_NUMBER);

Now to be fair, you can accomplish the same thing in other languages – you can define constants in place of magic numbers, and define your own debug printf which detects the environment and does something different based on OS or other factors. But typically other languages push this decision making process into runtime, impacting performance.

When it comes to macros, sure, you can do all kinds of stupid things – for example,

#define MIN(a, b) ((a) < (b) ? (a) : (b))
foo = MIN(var1, func1(args));

If the return value of func1 can vary between function invocations, for example it accesses a shared data structure – you just set yourself up for a big fat race condition that could take you days to hunt down. Good luck with that.

All things considered, the preprocessor provides power and flexibility I’ve often found missing in other languages.


Ok, obviously all the other languages out there have some concept of references or pointers… but C thrives in weird pointer math and pointer calculations. What other language lets you get away with code like

((unsigned char*)dword_pointer)[byte_offset] = byte;

Now at this point purists will usually be panicking and hyperventilating at the sheer craziness of such an attempt – why would ANYONE EVER want to commit such an act of sedition?!! Well, because sometimes you do. Maybe you have code that needs to iterate through an array a dword at a time, but this one function needs to change a specific byte. Heck if I know, point is this kind of flexibility rocks.

Another way this is manifest is in C’s handling of multi-dimensional arrays – or rather, C’s lack of handling multi-dimensional arrays.

int foo_array[5][10]
int bar_array[50];

foo_array[43] = 10;
bar_array[43] = 10;

Both statements above set element 43 (byte offset 172) to 10. C doesn’t complain, because all arrays are really just pointers to virtually contiguous blocks of memory.

For that matter, you can even do the following:

struct foo bar;
unsigned char * wacky_ptr = (unsigned char*)&bar;
*(wacky_ptr + 10) = 5;

Holy crap batman, what kind of blasphemy is going on here? I’m taking a pointer to a struct, typecasting it to a byte pointer, and setting byte 10 of the structure to 5. Why would anyone ever want to do that?!!

Who cares?

Point is, you can. And there are legitimate, actually useful reasons for wanting to do this. Sure, you can abuse this flexibility and write completely useless and unmaintainable code… but in the right hands, this type of flexibility works wonders.

And that really is the crux of the issue for me. C affords you all kinds of power and flexibility not present or possible in other languages. You want to inline assembly and muck around with processor registers? Sure, go ahead, whatever. You want to #define printf to be an infinite loop and confuse the heck out of your coworkers? Why the heck not, it’s your choice Mr. Programmer. You want to do crazy weird things with pointers? Be my guest!

The reality is, C seperates the men from the mice. Coders from posers. The flexibility C provides can be just enough rope to hang yourself – or in the right hands, it can be a powerful tool that creates amazing and beautiful software.

And that is what I love about C.

Next time tune in for what I hate about C πŸ™‚



Categories: Uncategorized Tags: , ,
  1. PNetwork
    May 12, 2010 at 1:07 pm

    You do know that the asm statement is an extension provided by the compiler you use, and not available in standard C? And how the asm statement is actually used and interpreted is also specific to the compiler?

    • May 12, 2010 at 1:59 pm

      Excellent point, which makes its use somewhat limited due to portability issues. Regardless, I think it’s an awesome part (extension) that makes C awesome. πŸ™‚

  2. May 12, 2010 at 4:11 pm

    C’s macros are powerful, but pale in comparison to Lisp. You can essentially rewrite the entire language with Lisp Macros and rewrite any bit of code you need. Performance wise, C might win out, but power-wise, nothing can conceptually come close to Lisp.

    But yeah, I like C and want to relearn it. Haven’t used it since college.

    • John
      May 13, 2010 at 7:31 am

      Well, he listed Scheme first in his list of languages, so I’d assume he’s familiar with the power of Lisp macros. Regardless, you make a good point for those who don’t know Lisp yet.

  3. Jeff Dege
    May 12, 2010 at 4:27 pm

    If your code includes embedded assembly, you’ve pretty much written off portability, at least in that source file.

    Of course, you can always #ifdef around the assembly, providing tuned assembly for some platforms and defaulting to a plain C equivalent for the rest.

  4. mark
    May 12, 2010 at 4:44 pm

    The problem is … C is not really a 2010 language …

    I think everything C could do, at least if it is really USEFUL, could be done by a language which is:

    – easier
    – more elegant

    • May 12, 2010 at 4:48 pm

      *shrug* OSes are pretty useful, so are drivers and firmware. I have yet to see any of those written in python or .NET. πŸ™‚

      • Dag
        May 13, 2010 at 7:17 am

        It’s quite refreshing to see someone who appreciates C, and realizes that it gets the job done.

        It is quite fashionable to renounce C in favour of something else these days. Sure, it’s old, and it doesn’t have all the fancy stuff. C++ does, and look how that turned out in the hands of non-computer scientists. Languages are made for mathematicians, biologists etc.

        And in the recent power-struggle between Git (C) and alternatives written in apparently vastly superior languages such as Darcs (Haskell) and Mercurial (Python), it is fun to watch how well C competes. This, in a domain where execution speed isn’t critical.

      • May 13, 2010 at 6:28 am

        Thanks for the comment. Yes, it is very fashionable to hate on C these days… and for userspace apps on multi-GHz processors, and where development time is more critical than execution time – sure, use an easier/newer/cooler language.

        If you need to write an OS, driver, filesystem, firmware, or video/audio codec… C is still king.

    • John
      May 13, 2010 at 8:25 am

      Every programming language that is “easier” and “more elegant” was written in C. C is the rock on which everything else is built.

  5. Musa
    May 12, 2010 at 7:37 pm

    I agree with ‘asm’ is quite strong, and its usefulness to a particular language almost unique to C. The macro language is untyped and has a trivial syntax. The preprocessor mechanism is a straightforward substitution. i.e. everything you can do with the macro/preprocessor “that comes with your C compiler”, can be done by some other preprocesssor. IMO void* is the holy grail here.

    • Danukeru
      May 12, 2010 at 11:03 pm

      I couldn’t agree more.

      The power you wield with a void pointer is vast.

      But at the same time if you don’t understand pretty thoroughly what you’re actually doing on multiple levels against whatever abstractions are provided to you by your OS (if you have one) or the very hardware you’re working on…you won;t just end up shooting yourself in the feet…you may as well just the whole foot off.

  6. Daniel
    May 13, 2010 at 1:04 am

    Actually, C doesn’t have inline assembler. It’s a non-standard extension, as far as I know.

    The D Programming Language (long name necessary for google searches :P) has standardised inline assembler. It doesn’t have a pre-processor, but what you did above can be accomplished with its version and debug constructs (think #ifdef) and static if (think #if EXCEPT that static if can evaluate expressions involving constants, function-calls, etc.).

    It also has pointers.

    On the other hand, it has lots of things C doesn’t. It has proper arrays (that is, arrays which know their own length as opposed to simply being syntax sugar for pointers), all strings are Unicode, fixed size integer types, classes, templates (which are significantly more powerful than C++’s *and* less confusing to boot), delegates, function literals, etc., etc.

    If nothing else, D serves quite well as a “better C” language.


    (P.S. stay away from D2: it’s not finished yet and very, very experimental.)

    • May 13, 2010 at 4:59 am

      Right, I have looked into D before and it does seem rather neat. However, unless you can compile it into the Linux kernel I won’t be able to use it much. Thanks for the reminder

      • Daniel
        May 13, 2010 at 7:37 am

        Excluding politics, you could, in theory.

        The problem is that the kernel maintainers probably wouldn’t want the added overhead of D’s runtime type information or the extra runtime support functions (things like associative array functions, array concatenation, etc.).

        But given that any D function can be written to use the C calling convention and name mangling, I don’t see any *technical* reason why you couldn’t. πŸ˜›

  7. Keith Thompson
    May 13, 2010 at 1:55 am

    Um, did you try the foo_array / bar_array example? It doesn’t even compile.

    And no, arrays most definitely are not just pointers (though a couple of language features conspire to make them look that way in many contexts). See section 6 of the comp.lang.c FAQ, http://www.c-faq.com.

    • May 13, 2010 at 4:57 am

      No, I didn’t try to compile it… it’s just an artificial example to prove a weak point. I’ll make sure my future examples compile – thanks for the heads up πŸ™‚

      • Keith Thompson
        May 13, 2010 at 8:30 am

        (I meant to mention that I wasn’t just talking about the missing semicolon.)

  1. May 13, 2010 at 6:13 pm
  2. January 2, 2011 at 8:13 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: