I continue seeing Uninitialized Data errors pop up in my code. What are they? How can I avoid them? What kind of problems do they cause? Is it a hardware issue or a Software Issue?
Depends on your environment. For example, some languages and some operating systems are designed so that accessing uninitialized data is a fatal error. For our example, let's use C/C , and the Win32 environment (NT, Win2000, 95, 98, and ME).
First, what is uninitialized data in C/C ?
Local variables when you first enter a function are uninitialized. Likewise variables that are declared in a lower scope are uninitialized, each time you (re-)enter that scope. Buffers returned by malloc, operator new, and similar functions are uninitialized till you write to them. Buffers returned by calloc are initialized (to zero). Buffers allocated by LocalAlloc, HeapAlloc, and others are uninitialized unless you specify flags to specifically initialize them.
Second, what data is in an uninitialized area ?
Theoretically, any pattern of 8bit random data is possible. In practice, it's quite predictable for a given environment, but will tend to be different for debug and release builds, and counting on the pattern is just asking for problems. One reason is that a minor change in some other part of your program may change the (accidental) pattern. For local variables, the usual pattern is whatever happened to be at that stack location the last time it was used by the same or other variable.
Third, what happens when you use such a variable?
If it's an integer, you just get an unexpected value. It may end up being unreasonably large (you just got a raise to $40000 per hour), or another illegal value (you have 43 cents in nickels, in your pocket). Other parts of your program far removed from the value may blow up in unexpected ways (you just accessed the 700th element of a 10-element array).
If it's a float or double, you run the risk of it's being an illegal value, which will throw an exception, or just an unreasonable one, see above.
If it's a pointer, you run the risk that you will access data that's unrelated to your intended purpose. This can cause corruption that might only become evident much later in the program. The best that could happen is that the pointer is accidentally zero, and you'll notice that and handle it. Almost as good is that it will contain an invalid address, and you'll get an exception dereferencing it.
Fourth, how can you detect uninitialized values ?
Make a habit of always initializing a variable at definition time
(i.e. avoid the bug)
In the case of classes, always declare a constructor, giving all objects a minimum valid state. Make sure those constructors initialize all the fields, as a "mostly initialized" object is one of the most painful bugs to track down. This means that every time you add data members to a class, you must immediately update all the constructors. Use new() rather than malloc() to allocate space for objects. When defining built-in types, always give them some value right away
Make use of an automatic initializer that gives it an "improbable" value, likely to be an obvious problem or exception
In most compiler's runtimes (and now I'll just refer to MSVC version 6), malloc and new are defined in a debug build to fill the buffer with a debug pattern. This pattern is something like FDFDFDFD... which tends to be obvious when you see it in a debugger, and will almost invariably give an exception if you use it as a pointer. In addition in a debug build, local variables are initialized with a similar pattern when you enter a function. But they don't get re-initialized when you change scopes, nor are the various kernel32 allocators so primed. Notice that I'm not recommending special initializers that work in release mode as well. I have seen global operator new redefined to do a zero-fill initialization, and I consider that bad practice. It's acceptable one class at a time, but only if you think carefully about it
Write code that self-validates likely trouble-spots
This is really a more general applicable approach to specific trouble areas. You write an "IsValid()" member function for each significant class, and invoke it in an assert at key points. Something like:
You also can use this approach to validate everything inside a collection, as a common problem in STL usage is improper copying of items.
Use a tool that detects them.
BoundsChecker is, of course, the best tool to detect such errors. Nevertheless, some may wish to augment BoundsChecker another tool. One such tool that I would mention is your compiler. Boost your warning level up to 4 (/W4), and use the /WX option to make any compile warnings fatal. You'd be surprised how often that will catch certain types of errors, and how much time it will save you later