A Short Guide to understanding C++ compiler errors

From Wikiid
Revision as of 05:36, 29 November 2010 by SteveBaker (Talk | contribs) (Created page with 'Sometimes the error messages produced by the C++ compiler seem cryptic - or unrelated to the actual bug that you're seeing. This document is an effort to help you find your prob…')

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Sometimes the error messages produced by the C++ compiler seem cryptic - or unrelated to the actual bug that you're seeing. This document is an effort to help you find your problem when that happens.

Compiling versus Linking

C++ (and most other compiled languages) convert your source code into an executable in two stages "Compiling" and "Linking". Each ".cpp" file in turn is compiled (converted from C++ source code to machine code instructions in binary) - and when all of them compiled sucessfully, the resulting ".o" (or ".obj" in Windows) files are collected together with all of the library files (".a", ".so" in Linux - ".lib", ".dll" in Windows) to make the final executable.

Most errors are detected at compile time - and are reported as errors in the ".h" and ".cpp" files. But sometimes an error may not be detected until the program is being linked - and those kinds of errors are usually reported as problems in the .o/.obj/.a/.lib/.so/.dll files. Linker errors generally occur because some kind of promise that you made in the source code for one .cpp file was broken. Hence, for example, if you declare that some object (class, function or variable) exists - but never actually provide an implementation for it - then the compiler can only know that this happened when it collects all of the binary files together and can finally see that something is missing. Another kind of problem that only the linker can find is when a function has accidentally been written more than once in different files.

Compiler confusion

When you make a mistake, the compiler is being fed something that doesn't fit the language it is expecting to see. This can easily confuse it.

When it finds that something is wrong, it will try to guess what you actually meant - do some kind of a fixup - then try to carry on and compile the rest of the code. However, very often it guesses wrong and then you can get a whole cascade of error messages that are truly meaningless.

So by all means, read the first error message that comes out of the compiler and try hard to fix it. Then take a glance at the second error and see if the problem it's complaining about is obviously your fault...but if it's not, don't sweat it - just kick the compiler off again and repeat the process. However, sometimes the compilation process itself is slow (such as if you are working on a header file that's included in a gajillion places - but which is causing consequential errors in only the 100th .cpp file!)...in that case, waiting for the compile to run again is going to take a while and you might want to spend your time productively in looking at the errors further down the source file.

Often, the compiler finds an error - gets confused - reports a bunch of bogus errors as a result - and then recovers (perhaps at the end of the function or class). So if you've fixed the first error - and the second one looks nonsensical - you can mentally skip over the errors in lines that follow close behind the one that's broken. A typo that produced a cluster of half a dozen errors within a dozen lines probably didn't cause the error that's reported a hundred lines later.

So if you have errors reported (say) on lines 6,7,9,11 and then 81,83,86 then the odds are that the error on line 6 is easily fixable, the ones on 7,9 and 11 are consequences of that and can be ignored pending a recompile - but the error on line 81 is very likely to be real and fixable too.

But if in doubt, just recompile.

Think like a compiler

If I write:

  far ( i = 0 ; i < 10 ; i++ ) { ... }

(I typed "far" instead of "for"). The compiler probably thinks that "far(i=0" is the start of a call to a function called "far" with its' parameter being the result of evaluating "i=0" and therefore it expects the next thing after the '0' to be either a close bracket or a comma and another parameter. It certainly doesn't expect a semicolon.

GNU C++ under Linux actually reports two errors on this line:

 c.cpp: error: expected ‘)’ before ‘;’ token
 c.cpp: error: expected ‘;’ before ‘)’ token

So the first error makes perfect sense - it says that it expected a ')'. What it probably did was to mentally stick a ')' before the ';' in order that it can try to carry on compiling...but then it's compiling:

 far ( i = 0 ) ;
 i < 10 ;
 i++ )

...and 'i<10;' is a perfectly legal (if somewhat useless) statement. But that makes 'i++ )' a syntax error because the compiler now expects a semicolon where the close bracket is.

The moral of the story is that the compiler is a relatively simple-minded program. It can't tell that you misspelled "for" as "far". So when you get an error you don't understand, you may have to look further back along the line - or possibly even up a line or two of code to figure out what broke.

The compiler 'flattens out' your includes and defines

Sometimes you'll get an error near the top of a header file - or in a cpp file just after all of the '#include's. You stare at the code until your eyes bleed and you can't see what the heck is wrong.

The problem is likely to be at the very end of a completely different header file.

The reason is that the compiler first expands out all of the '#include' directives by placing the contents of that file into your main file...then it starts to look at the syntax it finds.

Hence, if you have a header file ("myHeader.h") that says this:

 class X
   int x ;

(which is missing the final semicolon)...and "myOtherHeader.h" that say:

 class Y
   int y ;
 } ;

(which is perfectly OK)

Then when you say:

 #include "myHeader.h"
 #include "myOtherHeader.h"

...the error will probably be reported in the first line of "myOtherHeader.h". That's because it would have been perfectly legal (although a little crazy) to have put that semicolon on the first line of myOtherHeader.h to finish the declaration that was started in "myHeader.h"!!

This kind of thing can often be really nasty to track down because the error message can refer to files you haven't even touched!

 #include "myHeader.h"
 #include <stdio.h>

...would report the error as occurring inside the system header file "stdio.h" - which you know definitely can't have an error in it!!