Why Compiler Warnings are Your Friends

Even if you've fixed every compiler error and linker error your compiler gives you, it may still give you "warnings" (the cheeky bastard). These warnings won't keep your code from compiling (unless you ask the compiler to treat warnings as errors), and you might be tempted to just ignore them. There are, however, some good reasons not to do so. In doing so, I'll also cover some specific examples of the types of warnings you might face, what they mean, and how to fix them.

Catch Bugs Before Testing

The great thing about compiler warnings is that they are often indicators of future bugs that you would otherwise see only at runtime. For instance, if you see your compiler complaining about an "assignment in conditional" then it might mean that you've written
if ( x = 5 )   //uh oh, x = 5 will evaluate to the value 5, which is always true
{
        /* ... */
}
when what you really meant was
if ( x == 5 )
{
        /* ... */
}
You might have figured this out when your code always entered the if statement, but you wouldn't have known exactly why it was doing that. Maybe x wasn't being set properly (for instance, it might have been uninitialized). A scarier situation is one in which you never actually tested that branch of the if statement when x wasn't equal to five. This means that you would have a bug sitting in your code after you thought it was done. So the compiler warning both tells you exactly what is wrong and alerts you to a problem you might never have found!

Just for fun, here's an even more compelling example of when you'd have trouble finding the bug during testing.
if ( x = 0 )
{
        std::cout << "x is zero!";
}
Since x would generally not equal zero, the if statement generally wouldn't be expected to execute. Now, since x = 0 evaluates to false, this statement will always be false. If you never test the situation where x actually is 0, then you won't notice that the body of the if statement never executes.

Catch Bugs that are Hard to Find in Testing

Compilers will always warn you about things that might be difficult to find during testing. For example, your compiler can warn you that you are using an uninitialized variable; this can be hard to find in testing if you declare your variable far from the spot you first use it. (Note that "using" an uninitialized variable simply means getting its value; it's perfectly fine to initialize it. In fact, that's the solution to the problem.)

Another insidious bug is forgetting to return a value from a function. Sometimes this can be tricky to spot even once your compiler points it out. This might happen when your function has multiple paths through the code and most of them return a value; it's left up to you to find the one that doesn't.
int searchArray(int to_search[], int len, int to_find)
{
        int i;
        for( i = 0; i < len; ++i )
        {       
                if ( to_search[i] == to_find )
                {
                        return i;
                }
        }
}
Notice that most of the time, this function will return a value. It's only when the value isn't in the array that you wouldn't find it. This bug can be hard to spot in later testing both because this may be an uncommon occurrence and because the results of not returning a value when one is expected can be extremely weird. For instance, you might get a valid index of the array, or you might get something way too big (more likely). In either case, the problems in the running program wouldn't appear in the function at all.

Lessons to Take Away

While you've picked up a few tips for combating compiler warnings, the real message here should be the mindset you should have--catch bugs as early as possible, and take advantage of your tools, like the compiler, that tell you exactly where problems are before you discover something mysterious during testing. Sometimes there may be no need for you to set your compiler to fail on any warnings, but if you use automated scripts for building your program or tend to ignore the compiler unless it fails, then this might be a useful thing to do. You'll have to look at your compiler documentation for exactly how to set this up; on gcc and g++, all you need to use is the -Werror flag ("treat warnings as errors").

Furthermore, if you don't understand what a compiler warning means, it's probably best to trust that the compiler is telling you something valuable. I've had experiences where I was convinced that my code was correct and wasn't entirely sure what the compiler could be complaining about. But after investigating my compiler's complaints, I realized I had made a subtle mistake in a boolean expression that would have been nearly impossible to hone in on when debugging--it was much easier to catch the mistake while the code was fresh in my mind code rather than hours or possibly days later when I might (with luck) have found the bug due to the mistake.
Related articles

Tips and tricks from combating compiler and linker errors

Read more about the compiling and linking process