As I have been using C for over 30 years, I am glad that it is still very popular among embedded developers. Somehow, it has never been usurped by C++; it is unclear whether other languages, like Rust, might gain a greater foothold in due course. When I first learned C, it was only just beginning to be used for embedded programming. We had decided that offering training classes in embedded software development would be a good idea [as nobody else was doing it], so I really learned the language in order to teach it …
A good way to really get to grips with a subject is to be committed to teach it! We went about the planning of the training in a somewhat back to front manner: We decided on the dates for the first series of classes. We designed a brochure and planned the marketing. Only then did I start on developing the course. This venture was quite successful and I still claim that we were almost definitely the first to offer embedded programming training in Europe, if not the world!
In developing the course, I ensured that all the key topics were covered and I needed to consider how I might respond to all types of questions that I might be posed in the classes. Of course, I knew that I would inevitably get surprises and do a lot of “on the job” learning, but I endeavored to preempt many of them by checking out all the small details of the language.
I have always found C fascinating because it is quite a small language, but is very expressive. You can do almost anything in C. The hard bit is writing that code in a clear, readable and maintainable fashion – whilst still being efficient. Although I contend that a modern compiler helps with generating efficient code, understanding the details does help. I would like to suggest an example: the ++ and — operators. On the surface, these operators are very simple. Their job is to increment and decrement – add or subtract 1 from something. But that is far from the whole story …
First off, the operators come in two forms: preincrement and postinvrement [and the decrements]:
- ++i adds one to i and returns the result
- i++ returns the value of i and then increments i
This is quite straightforward and choosing the right one to form a more complex expression is not difficult. But, what if you are not using the returned result? Surely it does not then matter which one you use? Wrong. It does matter. A common situation where this arises:
for (i=0; i<5; i++) ...
This loop would work just fine with i++ or ++i, so why does it matter? It matters because embedded developers are interested in more than just functionality – we are always interested in overheads. Think about what code might be compiled for each form of the expression. For ++i, the variable is simply incremented; for i++, the value needs to be stored somewhere and then the variable is incremented. This means that the post increment form needs some additional storage, which is an unwanted overhead.
Of course, for a simple integer, the overhead is quite small. But, if the variable were actually a complex C++ object, it might be a more serious matter. It can be argued that a good compiler would observe that the expression result is not needed and this code would be optimized away. I sincerely hope that this would be the case. However, it is sloppy programming to just rely on compiler optimization to address the ignorance or laziness of the programmer.
Another surprise with these operators is their behavior with simple objects that are not integers and the confusing one is pointers. Consider this code:
int *p; int a; p = a; // p points to a p++; // p now points to a
I am guessing that this should not hold any surprises for most C programmers, but it is worth pondering what is actually going on. Initially p holds the address of the first element of the array a. After incrementing, it holds the address of the second element. On a 32-bit CPU, these two address will be have values that differ by 4. So the i++ added 4 to the pointer. This is logical and sensible, as the operator “knows” the size of the data being pointed to. But it illustrates that “incrementing” does not mean adding 1.