C In an Embedded Environment
Just the Facts
Before we get into the details of the C programming language, we first need to establish the facts about C and also bust a few of the myths that continue to be believed by many programmers.
C was originally developed at Bell Labs by Dennis Ritchie for use in developing operating systems. The first major use for C was in writing the Unix operating system back in 1974. Due to the low-level nature of operating system programming (think device drivers), C has a number of features that are a real benefit to the embedded systems developer. When you really think about it, every time we write code for an embedded microcontroller, we are essentially writing an operating system for that platform.
A big advantage of C today is that there are compilers available for virtually every microcontroller and computer platform. So, if you know C, you will have the programming skills needed to write code for a very wide variety of devices.
Busting the Myths
Over the years, there have been a lot of myths perpetuated about what C will do for you. The number one reason most people cite using C in the embedded world is that it is extremely portable due to the vast array of compilers available for all the different platforms. The problem is that C isn't really portable. For example, can a program that simply toggles an I/O pin on a PIC18F4520 toggle an I/O pin on the microprocessor inside your PC? Absolutely not. The ways in which you work with memory, handle interrupts, and use the on-chip hardware are completely different. Even if you try to move from one compiler to another for the exact same device, you will have problems. The way you declare an interrupt service routine using the XC8 compiler for a PIC18 is very different from how you would do it using the CCS compiler because the embedded specific features are not part of the language standard and cannot be avoided.
The core ANSI C language itself and any general algorithms you write that doesn't interface with the hardware will be portable. But in any embedded system, you will always make use of some of the on-chip peripherals and interrupts and there is no standard way of handling those and nor could there be.
Despite what many expert C programmers might tell you, C will NEVER be as efficient as assembly language. A good assembly language programmer will be able to outperform the best C compiler in most cases. Due to the required C runtime environment, C will always use more memory than a well-written assembly language program.
Now That's Just Crazy Talk
Something that I have heard from a number of self-proclaimed C gurus that strikes terror into my heart, is that C is "self-documenting". I cannot emphasize enough that there is no such thing as self-documenting C code. It doesn't matter how well-structured a piece of code is or how well its variables and functions are named. If you come back to the code you wrote one year ago, you will have trouble figuring out exactly what you were doing without comments.
C is a very flexible language that enables you to write some very powerful but terse lines of code. On their own, they are not too difficult to work with but once you combine many lines of code like this, a program can quickly grow to be totally incomprehensible. In fact, there is an annual contest (The International Obfuscated C Code Contest) where the most incomprehensible, yet elegant and functional code wins the prize. It is surprisingly easy to write incomprehensible code in C, though doing it at the level of the IOCCC contestants requires incredible skill and cleverness.