tags:

views:

228

answers:

5

This question isn't so much a 'how to solve' question as its a question about why doesn't this work?

In C++ I can define a bunch of variables that I want to use across multiple files in a few ways.

I can do it like this:

int superGlobal;
#include "filethatUsesSuperglobal1.h"

int main()
{
  // go.
}

That way ONLY works if "filethatUsesSuperglobal1.h" has its entire implementation there in the header and no attached .cpp file.

Another way (the "morer correcter" way) is to use extern:

externvardef.h

#ifndef externvardef_h
#define externvardef_h
// Defines globals used across multiple files.
// The only way this works is if these are declared
// as "extern" variables
extern int superGlobal;

#endif

externvardef.cpp

#include "externvardef.h"
int superGlobal;

filethatUsesSuperglobal1.h

#include "externvardef.h"
#include <stdio.h>
void go();

filethatUsesSuperglobal1.cpp

#include "filethatUsesSuperglobal1.h"
void go()
{
  printf("%d\n", superGlobal );
}

main.cpp

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

#include "filethatUsesSuperglobal1.h"

int main()
{
  printf( "%d\n", superGlobal ) ;
  go() ;
}

This is a small point and a somewhat nit picky question, but Why do I need to declare it extern - should not the #include guards on "externvardef.h" avoid redefinition?

Its a linker error, even though that header file has #include guards around it. So its not a compiler error, its a linker error, but why.

+15  A: 

Think about if from the code perspective - there is this symbol superGlobal that points at an integer.

You have a bunch of .o files to link together into a single executable. Each of the .o files has it's own superGlobal symbol. Which should the linker use?

The extern declaration says: another of the compilations units (.o files) is going to declare this symbol, so I can use it. Thus there is only one copy so the linker knows what to do.

Douglas Leeder
+4  A: 

the #ifndef guard only works on a per-file basis. So if you leave the extern off, then you get superGlobal defined 3 times, once when compiling each of the files main.cpp, filethatUsesSuperglobal1.cpp, and externvardef.cpp.

Keith Randall
Shouldn't the #ifndef guard stop ANYTHING from that file from being evaluated?
bobobobo
Well, no. The #ifndef guard stops everything inside it from being evaluated the second and subsequent times it is encountered during a compilation unit. All #defines get reset for each .cpp processed.
Keith Randall
+2  A: 

You are violating the One Definition Rule (ODR). A given variable may only be defined once.

When the header says "int SuperGlobal;", each file that includes the header defines the variable. When the header says "extern int SuperGlobal;", the variable is declared (so the various files know how to access it), but it will only be defined once - in an appropriate source file.

Jonathan Leffler
+1  A: 

The #ifndef guards only prevent the header from showing up more than once per cpp file. Once the compiler is done processing one cpp file, it starts over with the next, throwing away any knowledge of what happened while compiling the previous file. Each cpp file results in an object file, each of which has its own copy of the global variable. When the linker tries to tie everything together, it finds multiple definitions of where in memory to find the global variable. The linker has no way of knowing which object file to respect and which to ignore. Some linkers will just pick one and ignore all the others, others will barf. Either way, it's a mistake.

Darryl
+3  A: 

should not the #include guards on "externvardef.h" avoid redefinition?

The include guards protect against multiple declarations in a single compilation unit; wheras the extern declaration is required to prevent multiple definitions in a single executable unit.

Note the key thing here is to distinguish between declaration and definition. You will see this distinction in the error messages generated. Get the include guards wrong, and you get multiple declaration errors from the compiler. Get the extern wrong, and you get multiple definition errors from the linker. Notice also the roles of the compiler and linker in this.

Clifford