Home > C and C++ in critical systems > Making sure variables are initialized

Making sure variables are initialized

March 18, 2010

One source of program bugs is use of variables before they have been initialized. In C/C++ all static variables get zero-initialized if they have no specified initialization, so it is only local variables we need to worry about. Bugs caused by use of uninitialized local variables can be particularly nasty, because the value of such a variable depends on whatever previously occupied the same stack location. So the value read can vary depending on the past history of the program, and on the optimization level selected when the program was compiled (thereby causing debug builds and release builds to behave differently). An uninitialized variable bug that was benign and undetected in one version of a program can suddenly rear its ugly head when an unrelated change is made to the program.

We therefore need to ensure that all automatic variables are initialized before being used (MISRA C 2004 rule 9.1). Let’s consider the case of variables of primitive types first – we’ll look at structs, classes and unions later. Most static analysers can warn about simple cases like this:

void foo() {
int i;
...      /* code that doesn't assign i */
bar(i);  /* error, i not initialized */ } 

Many static analysers will also warn about the following:

void foo() {
int i;
if (b1) { i = 0; }
...      /* code that doesn't assign i */
if (b2) { bar(i); } } 

If b1 in the first if-statement was true whenever b2 in the second if-statement is true, then such a warning is a false positive. One approach when using such a static analyser is to add a dummy initialization for i, after satisfying oneself that the code is correct. Modern languages such as C# and Java use a concept of “definite initialization” that also requires a dummy initialization in this situation, otherwise the compiler will refuse to compile the program. However, ArC does a more thorough analysis and will only generate a warning if it can’t prove that b2 implies b1, thereby avoiding the need for a dummy initialization.

Here is a more difficult case:

void foo() {
int i;
bar(&i);  /* does this require i to be initialized first? */
fum(i);   /* has i been initialized yet? */ } 

The problem here is that we don’t know whether bar() expects its parameter to point to initialized memory or not, or whether bar() always writes to the variable addressed by its parameter. If bar() is a fairly simple function, then a static analyser may be able to work it out; but that isn’t always possible, especially if bar() calls other functions whose source is not available.

ArC gets round this problem by providing an out-parameter annotation. If bar() is declared with the following signature:

void bar(out int* p);

then bar() does not require p to point to an initialized variable, but it is obliged to assign *p prior to returning. As usual with ArC keywords, when the program is being compiled, out is defined as a macro expanding to nothing, so the compiler doesn’t object to this syntax.

What about structs and classes? If you’re using C++, then I recommend that you define at least one constructor in every class or struct declaration. This prevents you from declaring a variable of that type without a constructor being called. ArC requires a constructor to initialize all fields of the class or struct before returning, and will verify this.

What about unions? You may recall from my previous post that ArC considers each union to include a ghost discriminant field that can be interrogated using the holds operator. Well, ArC considers an uninitialized union to hold nothing. Therefore, attempting to access a member of an uninitialized union will always result in a verification failure.

  1. Dave Banham
    March 23, 2010 at 20:49

    Got to be careful about assumptions here. In some embedded systems the flooding of .bss with zeros is removed as a startup optimisation. So no variable should be accessed without being explicitly initialised, statically or dynamically.

    Regards
    Dave B.

    • March 23, 2010 at 21:42

      I’m surprised by this! Surely clearing memory to zeros is a very cheap operation on just about any
      processor these days? After a hard reset, some processors use 1000s of clock cycles performing self tests anyway. Initializing static variables explicitly will take much longer than clearing bss, unless only a small proportion of them need it. I’m strongly tempted to say that this type of violation of the C standard is unacceptable in critical systems. But maybe you can give an example of when you have found it necessary? If so, we could add a ‘no zero initialization’ switch to ArC.

  2. Dave Banham
    March 29, 2010 at 20:20

    Well I do confess that my personal experience of this was sometime ago now and I might add that the system in question also minimised its power on self tests to those deemed essential and did the rest as a continuous background self test. It had a performance requirement to restore service after a reset (or power on) that was quite onerous at the time. Nevertheless, you might want to consider adding the ‘no zero initialization’ switch to ArC (or similar) to flush out all the non-explicitly-initialised-before-being-accessed variables. (Static checks like PC Lint can do a basic check for this of course.)

    Regards
    Dave B.

  3. Duckly Ug
    June 22, 2010 at 10:04

    A system could have large input buffers, for example, a camera having a few MB of memory for capturing a 1280×1024 frame. Filling a few MB of SDRAM could take hundreds of milliseconds to a few seconds depending on memory/processor speed. These zeros would get overwritten by the actual camera data when we start capture.

    One may want to speed up boot up time to show the flash screen to the user or may be establish a link with other devices on the system.

    (I have read this page out of context; I’m simply illustrating a case when one may want to skip bss flooding with zeros.)

    • June 22, 2010 at 11:45

      That’s a good example. We’ll add a “no implicit zero initialization” flag to ArC if customers want it.

  1. No trackbacks yet.
Comments are closed.
<span>%d</span> bloggers like this: