Thought Leadership

5 embedded software programming tips

By Colin Walls

Most software developers will have received some kind of training or education in programming techniques. Others may be self-taught. But either way, many will agree that much of the real learning comes later. When programming, it is possible to stumble across useful techniques or get advice from colleagues during a code review. Even very experienced programmers pick up new tips from time to time.

This posting is the first in an occasional series in which I will outline 5 useful [I hope!] snippets of advice for embedded software developers …

Here, in no particular order, are today’s 5 tips:

Set a pointer to NULL after use

Pointers are very a useful and powerful language feature, but this power can result in errors. A common mistake is for code to make use of a pointer when its value is no longer valid. For example, it may point to some memory that had been dynamically allocated, but has since been relinquished. Using an invalid pointer can cause adverse effects that do not become apparent for some time and are, thus, a very hard to find bug. If you routinely set a pointer to NULL after its use is over, later erroneous use will cause an immediate error that enables the bug to be located easily.

In C, it is lazy programming to use int for unsigned data

In C, the int data type is almost a default. Indeed, in the original language definition, it was the default return data type for a function [which really should have been void, but that came later]. Most C programmers tend to select int for a variable unless it is very clearly inappropriate. I would argue that they should really elect for unsigned, as more data is unsigned than signed. It is best to consider exactly what the range of values is that you need to store. Is it signed or unsigned? Do you need 8, 16, 32 or more bits? Surprisingly, time/date counters have commonly be made signed, which had led to Y2K style bugs.

Beware of creeping elegance

When is a piece of software finished? The obvious answer is when it provides all the specified functionality, with no known bugs. There are a couple of circumstances in which completion may be compromised. Firstly, many engineers are perfectionists and can always see things to “improve” in their code. In their hands, without care and supervision, a project can over-run badly. Another, less obvious situation, is when some code has been written for a specific purpose/project and is then reused elsewhere. It is very easy to “build on experience” and make some improvements to the code prior to reuse. Without care, this can become a version control nightmare. I experienced this issue with an in-house RTOS many years ago.

With an RTOS, an event flag or signal is the most efficient way to send simple logic data

A modern RTOS, like our own Nucleus RTOS product, includes a great many facilities. Being scalable, such an OS enables the developer to pick and choose which facilities to use, without suffering a code size penalty from the unused facilities. Inter-task communication is important in any multi-threaded design, so a number of different capabilities are likely to be available and they should be selected with care. If one task simply needs to flag to another that something has occurred, the simplest communication method – event flags or signal – are likely to be the most efficient option.

Recursive code can look very elegant, but is very dangerous

A number of mathematical processes may be described using recursive functions – i.e. functions that directly or indirectly call themselves. This may be an apparently elegant way to solve a problem, that uses minimal code. Here is a simple example:

void printbase(int number, int base)
{
   if (number >= base)
   {
      printbase(number/base, base);
   }
   printf("%X", number%base);
}

Is it clear what this code does? The answer is probably “not immediately” and this is one reason to avoid the technique, Clarity of meaning in code is essential to aid further maintenance. Additionally, recursive functions make heavy use of the stack and this may go out of control without care. Stack overflows are quite subtle bugs to locate.

Comments

2 thoughts about “5 embedded software programming tips
  • Thanks for this “general embedded programming” entry I was longing for some time (being not really interested in RTOS at the moment). I still see a lot of embedded code using integers as default while, as you said, most numbers we need are never negative. I took this advice to myself long time ago and really endorse its use (together with declarations containing max size like uint8_t, etc.). I see problem with too much recursion use quite rare (at least in embedded codebase I see), maybe just because most developers still find it less intuitive than iterative approach. When I see recursion, I always think in a little stereotypic way “watch out, we have a TCS major here” who should remember that embedded ground is treacherous (this poor small stack).

Leave a Reply

This article first appeared on the Siemens Digital Industries Software blog at https://blogs.sw.siemens.com/embedded-software/2018/01/22/5-embedded-software-programming-tips/