4 handy embedded software tips

From time to time, I like to muse on some useful programming tips for embedded developers. This is such a time. I am not presenting hard and fast rules; I am just suggesting some possible good practices. I am also keen to promote discussion, as an alternate point of view is always interesting. Do contact me by comment or via email or social media …

Always use ++i instead of i++ if you are not saving the result of the expression

Every C programmer knows the difference between the prefix and postfix form of the ++ operator: ++i increments i and returns the new value; i++ increments i and returns the old value. They are both useful in various contexts. However, if you are not using the returned value – i.e. you just convert the the expression into a statement – does it matter which one you use? For example, you might write:

for (i=0; i<10; i++)
   ...

or

for (i=0; i<10; ++i)
   ...

They both do exactly the same thing, but is one better than the other?

The answer is: most of the time it makes no difference which form you use. If i is an int the resulting code would be identical. However, if i were an instantiation of a user-written class, and the ++ operator(s) had be overloaded, it would be a different story. The postfix form would probably result in some extra memory being used to accommodate the old value ready to return. Although compiler optimization might save the day, it is good practice to use the prefix form by default.

Never test for equality of floating point values

When floating point calculations are performed, there is always the possibility of tiny rounding errors. This results in code like this:

float x, y;
...
if (x == y)
   ...

being misleading. The result of some calculations may be that x and y are both 6.0. However, their actual values might be 6.0000001 and 6.0000002 respectively. So, the equality comparison would produce the wrong result. Clearly a comparison for less than or greater than would also need care.

Only have a single return point from a function; it makes for clear code

This tip is somewhat controversial and, to be honest, I could swing either way. The idea is that, there is only one entry point to a function: the top. There should, therefore, be a single exit/return point: the bottom. The thinking is that this makes the structure clear and avoids the possibility that any function exit code is overlooked.

The opposing argument is that, to achieve this structure, in some cases, may require some quite convoluted logic to steer execution to the bottom. The best approach is to perform parameter verification etc., which might result in an early exit, at the top and include one or more return statements there, if necessary. Then, enter the main body of the function, where there is a single exit at the bottom.

Coding standards like MISRA C encourage the single exit approach and it is mandated by some safety standards like IEC 61508 and ISO 26262.

Always initialize a variable at the point of definition. Never assume that it is zero

Even though the language standard says that static variables in C/C++ will always be initialized to zero, it is bad programming practice to assume that this is the case. If nothing else, readability of the code is improved by an explicit initialization. I feel that this is best done when the variable is declared, but it might be argued that the declaration are initialization are [for automatic variables anyway] separate things and that should be illustrated. So, looking at this code:

void fun()
{
   int alpha, beta=2;

   alpha = 3;
   ...

Which variable has been handled in the best/clearest fashion? The generated code will be the same either way.

Comments

3 thoughts about “4 handy embedded software tips
  • I would agree that multiple vs. single return statements in a function or method is controversial, I do not agree that having a single return statement is easier to read, I would argue that multiple return statements makes code easier to read and maintain with one issue:

    Releasing allocated resources prior to returning is always an issue when code is refactored or maintained. If a locking mutex has been acquired, every path that exits must release the mutex, as is the same issue with allocated memory for objects created that are not released by the compiler implimentation.

    When a function or method has switch() and case: statements, tossing in a break and later coming to a single exit point can truly look muddied when a case: statement could easily exit through a return, making the code less complex and easier to follow.

    By the way, initializing all auto stack variables to known values when they are defined assists LINT and CPPCheck locate pointers that may be left at NULL, but it detracts from LINT reporting that auto variables may be uninitialized. By initializing all auto variables, some compilers and some tools may not report that a variable is not used, leaving open the question whether the function or method’s functionality is complete or whether the variable is a left-over.

    Point being that there are trade-offs in everything. 🙂 Personally I always initialzie automatic stack variables when they are defined even if the next statement sets a variable’s value. If a variable is a floating point, I’ll set it to 0.0. If it is a long I’ll set it to 0L. If it is intended to be a bit mash I’ll set it to 0x0000.

    The idea is to make the maintenance of the cod easier for the next person, so while best practices and standards come and go, at the end of the day the methodology that assists a software person or an auditor to understand a function’s behavior and to allow the behavior to be altered with minimal risk is paramount.

Leave a Reply

This article first appeared on the Siemens Digital Industries Software blog at https://blogs.sw.siemens.com/embedded-software/2018/05/14/4-handy-embedded-software-tips/