The maintenance problems that uninitialised locals cause (particularly pointers) will be obvious to anyone who has done a bit of c/c++ maintenance or enhancement, but I still se
Yes: always initialize your variables unless you have a very good reason not to. If my code doesn't require a particular initial value, I'll often initialize a variable to a value that will guarantee a blatant error if the code that follows is broken.
Short answer: declare the variable as close to first use as possible and initialize to "zero" if you still need to.
Long answer: If you declare a variable at the start of a function, and don't use it until later, you should reconsider your placement of the variable to as local a scope as possible. You can then usually assign to it the needed value right away.
If you must declare it uninitialized because it gets assigned in a conditional, or passed by reference and assigned to, initializing it to a null-equivalent value is a good idea. The compiler can sometimes save you if you compile under -Wall, as it will warn if you read from a variable before initializing it. However, it fails to warn you if you pass it to a function.
If you play it safe and set it to a null-equivalent, you have done no harm if the function you pass it to overwrites it. If, however, the function you pass it to uses the value, you can pretty much be guaranteed failing an assert (if you have one), or at least segfaulting the second you use a null object. Random initialization can do all sorts of bad things, including "work".
This pertains to C++ only, but there is a definite distinction between the two methods.
Let's assume you have a class MyStuff
, and you want to initialize it by another class. You could do something like:
// Initialize MyStuff instance y
// ...
MyStuff x = y;
// ...
What this actually does is call the copy constructor of x. It's the same as:
MyStuff x(y);
This is different than this code:
MyStuff x; // This calls the MyStuff default constructor.
x = y; // This calls the MyStuff assignment operator.
Of course, completely different code is called when copy constructing vs. default constructing + assigning. Also, a single call to the copy constructor is likely to be more efficient than construction followed by assignment.
Let me tell you a story about a product I worked on in 1992 and later that, for the purposes of this story, we'll call Stackrobat. I was assigned a bug that caused the application to crash on the Mac, but not on Windows, oh and the bug was not reproducible reliably. It took QA the better part of a week to come up with a recipe that worked maybe 1 in 10 times.
It was hell tracking down the root cause since the actual crash happened well after the action that did it.
Ultimately, I tracked it down by writing a custom code profiler for the compiler. The compiler would quite happily inject calls to global prof_begin() and prof_end() functions and you were free to implement them yourselves. I wrote a profiler that took the return address from the stack, found the stack frame creation instruction, located the block on the stack that represented the locals for the function and coated them with a tasty layer of crap that would cause a bus error if any element was dereferenced.
This caught something like a half dozen errors of pointers being used before initialization, including the bug I was looking for.
What happened was that most of the time the stack happened to have values that were apparently benign if they were dereferenced. Other times the values would cause the app to shotgun its own heap, taking out the app sometime much later.
I spent more than two weeks trying to find this bug.
Lesson: initialize your locals. If someone barks performance at you, show them this comment and tell them that you'd rather spend two weeks running profiling code and fixing bottlenecks rather than having to track down bugs like this. Debugging tools and heap checkers have gotten way better since I had to do this, but quite frankly they got better to compensate for bugs from poor practices like this.
Unless you're running on a tiny system (embedded, etc), initialization of locals should be nearly free. MOVE/LOAD instructions are very, very fast. Write the code to be solid and maintainable first. Refactor it to be performant second.
Sometimes you need a variable as a placeholder (e.g. using the ftime
functions), so it doesn't make sense to initialize them before calling the initialization function.
However it wouldn't be bad, in my opinion, to annotate the fact that you are aware of the pitfalls, something in the way of
uninitialized time_t t;
time( &t );
It should be mostly mandatory. The reason for this has nothing to do with performance but rather the danger of using an unitialized variable. However, there are cases where it simply looks ridiculous. For example, I have seen:
struct stat s;
s.st_dev = -1;
s.st_ino = -1;
s.st_mode = S_IRWXU;
s.st_nlink = 0;
s.st_size = 0;
// etc...
s.st_st_ctime = -1;
if(stat(path, &s) != 0) {
// handle error
return;
}
WTF???
Note that we are handling the error right away, so there is no question about what happens if the stat fails.