10.3. Guidelines for Initializing Variables

 < Free Open Study > 

Improper data initialization is one of the most fertile sources of error in computer programming. Developing effective techniques for avoiding initialization problems can save a lot of debugging time.


The problems with improper initialization stem from a variable's containing an initial value that you do not expect it to contain. This can happen for any of several reasons:

  • The variable has never been assigned a value. Its value is whatever bits happened to be in its area of memory when the program started.

    Cross-Reference

    For a testing approach based on data initialization and use patterns, see "Data-Flow Testing" in Section 22.3.


  • The value in the variable is outdated. The variable was assigned a value at some point, but the value is no longer valid.

  • Part of the variable has been assigned a value and part has not.

This last theme has several variations. You can initialize some of the members of an object but not all of them. You can forget to allocate memory and then initialize the "variable" the uninitialized pointer points to. This means that you are really selecting a random portion of computer memory and assigning it some value. It might be memory that contains data. It might be memory that contains code. It might be the operating system. The symptom of the pointer problem can manifest itself in completely surprising ways that are different each time that's what makes debugging pointer errors harder than debugging other errors.

Following are guidelines for avoiding initialization problems:

Initialize each variable as it's declared Initializing variables as they're declared is an inexpensive form of defensive programming. It's a good insurance policy against initialization errors. The example below ensures that studentGrades will be reinitialized each time you call the routine that contains it.

C++ Example of Initialization at Declaration Time
float studentGrades[ MAX_STUDENTS ] = { 0.0 };

Initialize each variable close to where it's first used Some languages, including Visual Basic, don't support initializing variables as they're declared. That can lead to coding styles like the following one, in which declarations are grouped together and then initializations are grouped together all far from the first actual use of the variables.

Cross-Reference

Checking input parameters is a form of defensive programming. For details on defensive programming, see Chapter 8, "Defensive Programming."


Visual Basic Example of Bad Initialization

' declare all variables Dim accountIndex As Integer Dim total As Double Dim done As Boolean ' initialize all variables accountIndex = 0 total = 0.0 done = False ... ' code using accountIndex ... ' code using total ... ' code using done While Not done    ...


A better practice is to initialize variables as close as possible to where they're first used:

Visual Basic Example of Good Initialization
Dim accountIndex As Integer accountIndex = 0 ' code using accountIndex ... Dim total As Double total = 0.0       <-- 1 ' code using total ... Dim done As Boolean done = False       <-- 2 ' code using done While Not done ... 

(1)total is declared and initialized close to where it's used.

(2)done is also declared and initialized close to where it's used.

The second example is superior to the first for several reasons. By the time execution of the first example gets to the code that uses done, done could have been modified. If that's not the case when you first write the program, later modifications might make it so. Another problem with the first approach is that throwing all the initializations together creates the impression that all the variables are used throughout the whole routine when in fact done is used only at the end. Finally, as the program is modified (as it will be, if only by debugging), loops might be built around the code that uses done, and done will need to be reinitialized. The code in the second example will require little modification in such a case. The code in the first example is more prone to producing an annoying initialization error.

This is an example of the Principle of Proximity: keep related actions together. The same principle applies to keeping comments close to the code they describe, keeping loop setup code close to the loop, grouping statements in straight-line code, and to many other areas.

Cross-Reference

For more details on keeping related actions together, see Section 10.4, "Scope."


Ideally, declare and define each variable close to where it's first used A declaration establishes a variable's type. A definition assigns the variable a specific value. In languages that support it, such as C++ and Java, variables should be declared and defined close to where they are first used. Ideally, each variable should be defined at the same time it's declared, as shown next:

Java Example of Good Initialization
int accountIndex = 0; // code using accountIndex ... double total = 0.0;       <-- 1 // code using total ... boolean done = false;       <-- 2 // code using done while ( ! done ) { ... 

(1)total is initialized close to where it's used.

(2)done is also initialized close to where it's used.

Use final or const when possible By declaring a variable to be final in Java or const in C++, you can prevent the variable from being assigned a value after it's initialized. The final and const keywords are useful for defining class constants, input-only parameters, and any local variables whose values are intended to remain unchanged after initialization.

Cross-Reference

For more details on keeping related actions together, see Section 14.2, "Statements Whose Order Doesn't Matter."


Pay special attention to counters and accumulators The variables i, j, k, sum, and total are often counters or accumulators. A common error is forgetting to reset a counter or an accumulator before the next time it's used.

Initialize a class's member data in its constructor Just as a routine's variables should be initialized within each routine, a class's data should be initialized within its constructor. If memory is allocated in the constructor, it should be freed in the destructor.

Check the need for reinitialization Ask yourself whether the variable will ever need to be reinitialized, either because a loop in the routine uses the variable many times or because the variable retains its value between calls to the routine and needs to be reset between calls. If it needs to be reinitialized, make sure that the initialization statement is inside the part of the code that's repeated.

Initialize named constants once; initialize variables with executable code If you're using variables to emulate named constants, it's OK to write code that initializes them once, at the beginning of the program. To do this, initialize them in a Startup() routine. Initialize true variables in executable code close to where they're used. One of the most common program modifications is to change a routine that was originally called once so that you call it multiple times. Variables that are initialized in a program-level Startup() routine aren't reinitialized the second time through the routine.

Use the compiler setting that automatically initializes all variables If your compiler supports such an option, having the compiler set to automatically initialize all variables is an easy variation on the theme of relying on your compiler. Relying on specific compiler settings, however, can cause problems when you move the code to another machine and another compiler. Make sure you document your use of the compiler setting; assumptions that rely on specific compiler settings are hard to uncover otherwise.

Take advantage of your compiler's warning messages Many compilers warn you that you're using an uninitialized variable.

Check input parameters for validity Another valuable form of initialization is checking input parameters for validity. Before you assign input values to anything, make sure the values are reasonable.

Cross-Reference

For more on checking input parameters, see Section 8.1, "Protecting Your Program from Invalid Inputs," and the rest of Chapter 8, "Defensive Programming."


Use a memory-access checker to check for bad pointers In some operating systems, the operating-system code checks for invalid pointer references. In others, you're on your own. You don't have to stay on your own, however, because you can buy memory-access checkers that check your program's pointer operations.

Initialize working memory at the beginning of your program Initializing working memory to a known value helps to expose initialization problems. You can take any of several approaches:

  • You can use a preprogram memory filler to fill the memory with a predictable value. The value 0 is good for some purposes because it ensures that uninitialized pointers point to low memory, making it relatively easy to detect them when they're used. On the Intel processors, 0xCC is a good value to use because it's the machine code for a breakpoint interrupt; if you are running code in a debugger and try to execute your data rather than your code, you'll be awash in breakpoints. Another virtue of the value 0xCC is that it's easy to recognize in memory dumps and it's rarely used for legitimate reasons. Alternatively, Brian Kernighan and Rob Pike suggest using the constant 0xDEADBEEF as memory filler that's easy to recognize in a debugger (1999).

  • If you're using a memory filler, you can change the value you use to fill the memory once in awhile. Shaking up the program sometimes uncovers problems that stay hidden if the environmental background never changes.

  • You can have your program initialize its working memory at startup time. Whereas the purpose of using a preprogram memory filler is to expose defects, the purpose of this technique is to hide them. By filling working memory with the same value every time, you guarantee that your program won't be affected by random variations in the startup memory.

 < Free Open Study > 


Code Complete
Code Complete: A Practical Handbook of Software Construction, Second Edition
ISBN: 0735619670
EAN: 2147483647
Year: 2003
Pages: 334

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net