Friday, 23 May 2014

Guideline 7. Always initialize variables

Guideline


Variables should be initialized with a known and meaningful value as soon as they are created. A variable should never begin to exist without having a known value.

Discussion


Initialize all local variables


There are three things you need to learn how to design well: functions, classes and projects. Initializing all variables is an issue in all of them.

  • In a function body, you need to initialize all local variables.
  • In a class, you need to initialize all member variables (static or nonstatic).
  • In a project, you need to initialize all global variables.

You may have noticed that these guidelines have been focusing in the function scope. By now, I will keep this focus and I will discuss only local variables in this guideline. I expect to discuss the design of classes and projects in future guidelines. However, it's worth noting that all variables need to be initialized, without exception.

The most common sign I've found that someone is a novice C++ programmer are uninitialized variables. You look at a function body and you see the definitions of many uninitialized variables, usually grouped at the beginning of the function. Sometimes they are assigned to later, sometimes they aren't used at all, and sometimes their uninitialized value is used, causing undefined behaviour.

This is bad. Very bad. It hurts readability and it is error prone. The risk of using the value of an uninitialized variable is never worth taking. Don't do it. Always initialize variables.

Initialization should be simple. If you need to do complicated things like asking the user for a value, you should do that in a separate operation and, if that succeeds, assign its result to your variable. Don't use lengthy, complex or difficult to predict operations in the variable initialization itself.

Example 1

Don't

int main()
{
   int i;
   double d;
}

The variables i and d have now unspecified values. Your program does not have undefined behaviour, though, because it doesn't read their values at any time. It is written in bad style, but it has a well defined behaviour: it runs without producing any external effect.

The main function is special in that if you don't write a return statement for it, it implicitly returns 0 when it returns. Your program takes advantage of this and, after running without any external effect, it ends with a return value from main of 0.

Let's see one variation in which the value of uninitialized variables is read:

#include <iostream>
using std::cout;

int main()
{
   int i;
   double d;
   cout << "i: " << i << " ";
   cout << "d: " << d << " ";
}

Great. Now your program has undefined behaviour. Are you satisfied?

You'd rarely write code such as this. But in some cases the use of an uninitialized variable may not be so obvious. For example, you could write f(d); in main, with f being defined in some included header file. At first sight you wouldn't know if you're getting into trouble by passing a still uninitialized variable to f, because you don't know if f uses the value of that variable. However, if you don't like getting into trouble, you'll avoid passing uninitialized variables to functions - and the best way to do that is by not having uninitialized variables in the first place.

Do

#include <iostream>
using std::cout;

int main()
{
   int i = 0;
   double d = 0.0;
   cout << "i: " << i << " ";
   cout << "d: " << d << " ";
}

I have modified your program so that it initializes i with an integer value, and d with a floating point value. Now you have a correct C++ program which sends the following to the standard output:

i: 0 d: 0

Isn't it beautiful?

Bibliography

See the page Bibliography for details of the referenced materials.

[McCONNELL 2004] This book discusses the initialization of variables in Section 10.3, "Guidelines for initializing variables".

[SUTTER-ALEXANDRESCU 2004] This book includes this recommendation as its rule 19, "Always initialize variables" (page 36).

No comments:

Post a Comment