5 more embedded software programming tips

Time for another bunch of tips for embedded software developers, which I hope are helpful. I will admit that, in many cases, they are just common sense, but I think we all need reminding from time to time. If you have any thoughts or suggestions on this topic, please contact me via comment, email or social media. Otherwise, 5 pearls coming up …

In C/C++, always put braces ({ }), both opening and closing, on lines by themselves and line up a corresponding pair horizontally

As, in C/C++, whitespace is not significant to the compiler, layout of code is totally up to the programmer. As Colin’s First Rule of Programming is “write code with the human reader in mind”, getting that layout right is essential. In a structured programming language, the vital thing to consider is the hierarchy – exactly what is dependent on an if, for example. You could write code like this:

if (a == b) {
   x = y;
   y = x; }

The indentation is correct, but, IMHO, the extra vertical space around the block makes this clearer:

if (a == b)
{
   x = y;
   y = x;
}

Even if you inline a function in C/C++, the compiler is likely to make an out-of-line copy

The facility to inline a function, so that the call to a function is replaced by the actual function code, is a handy way to improve code performance, even if it only makes sense for small functions. This can occur automatically, with the right compile time options or a hint may be given to the compiler by using the inline keyword. Some care is needed, as a compiler may also create an out-of-line copy of the code as well. This may be for 2 reasons:

  1. Some debuggers are not smart enough to handle inlined code, so they need an out of line copy to use at debug time.
  2. If the function is global, it may be used in other modules, where the compiler does not have access to the code to inline it.

The solution to (1) is a matter of toolchain selection. (2) can be fixed by making sure that such a function is declared as static.

Even though an assignment in C/C++ yields a value, it is very bad practice to embed one in an expression

I clearly recall, many years ago, when I was first getting to grips with C, that it was quite a revelation when I understood that an assignment was just an expression and an expression yielded a value. I was very keen to exploit this cool construct whenever I could. However, I was naïve and foolish and should have thought more about code readability. For example, you can write this:

if (a = b)
   fun(a);

The value of b is copied into a and, it it were a non-zero value [i.e. TRUE] the function call would take place. Alternatively, this could be written like this:

a = b;
if (a != 0)
   fun(a);

This is clearer and less error-prone. It is so easy to use = in a conditional when you meant to use ==.

As an aside, I think the code would be clearer still written like this:

a = b;
if (a != 0)
{
   fun(a);
}

Incidentally, the resulting binary code would be identical in every case.

An extra word at the end of a stack may be used to monitor overflow

One of the most difficult bugs [sorry, errors!] to find in multithreaded code is a stack overflow – the result of allocating too little stack space to a task or the code going badly awry. This is because the thread that really has the problem tends to continue happily, but corrupts memory used by another thread, which starts to behave badly even though it is not the cause of the problem. Obviously you will test your code thoroughly and be on the lookout for this kind of issue, but it is prudent to include some self-checking code in the application in case the problem manifests itself later. This is normally a matter of including a “guard word” just beyond the end of the stack. Any corruption of this word’s contents indicates that stack overflow has occurred and a problem is imminent. I wrote a detailed article on this topic a while ago.

Function prototypes are not mandatory in C. Just pretend that they are

In C++, every function must have a prototype, but this is not mandatory in C. I suggest that you pretend that it is! It is straightforward to do and provides some useful documentation and helps avoid errors. A “normal” prototype looks like this:

unsigned volume(unsigned, unsigned, unsigned);

However, you have the option of adding names to the parameters, thus:

unsigned volume(unsigned x, unsigned y, unsigned z);

or even

unsigned volume(unsigned width, unsigned height, unsigned depth);

These names have so use/significance elsewhere, but certainly can clarify the purpose of each parameter.

Comments

4 thoughts about “5 more embedded software programming tips
  • so is the case while writing the for or any other loop statements. example:
    for (int i=0; i<=Max_value; i++){
    printf("i=%d\t",i); // print the value of i in the same line
    }

    or more clear when your write it as:
    for (int i=0; i<=Max_value; i++)
    {
    printf("i=%d\n",i); // will print the value of i on new line
    }

  • i think the topic about the brackets is trivial, if you write:

    if(x==y) a=b;
    or
    if(x==Y) {
    a=b;
    }
    or
    if(x==y)
    {
    a=b;
    }

    i like my code compact so i preverd the first and the second code but it s in the mind of the styleguid or the project.
    What everyday is bad style is:
    while(2 != 3){//endless loop
    a = b
    }
    I prefer a style like this
    #define ENDLESS_LOOP for(;;)

    ENDLESS_LOOP
    {
    a=b;
    }

Leave a Reply

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