View Single Post
Old Jan 13th, 2007, 8:24 PM   #4
grumpy
Programming Guru
 
grumpy's Avatar
 
Join Date: Jun 2005
Location: Adelaide, South Australia
Posts: 1,254
Rep Power: 5 grumpy will become famous soon enough
You need to understand the distinction between a definition and a declaration. The following may seem a bit long, but it'll walk you through the things you need to know to fix your problem.

I'll use a global variable of type int to illustrate, but the idea's exactly the same if the global is of a different type (eg a pointer to a function).

Within a complete program (i.e. what is assembled by the linker, if it succeeds) there can only be one instance of a global. The way you ensure there is an instance is to define it. In a single source file, this is easy;
int global_var;

void function()
{
    //  do things with global_var
}

int main()
{
     // do things with global_var

     function();   // will do more things with global_var

     //  do even more things with global_var
}
In this, the declaration of the variable named global is also a definition: it ensures that all functions in the source file can access global_var, and it creates the thing for them to access.

But what happens if we break the project up into parts. First a header file declaring our global;
 // header file named   global.h
#ifndef GLOBAL_H_INCLUDED
#define GLOBAL_H_INCLUDED
    int global_var;
    int function();    // prototype for calling function()
#endif
And then our function....
// global.cpp    (assume cpp is an extension for C++ files recognised by our compiler)
#include "global.h"
void function()
{
    //  do things with global_var
}
and then our main function.
// main.cpp    (assume cpp is an extension for C++ files recognised by our compiler)

#include "global.h"

int main()
{
     // do things with global_var

     function();   // will do more things with global_var

     //  do even more things with global
}
Then what people would typically do is then create a project (with an IDE) or a make file which essentially includes ensures that each of the source files (.cpp in this case, .cc with some other compilers) are compiled into objects, and then the objects are linked with libraries to create an executable.

The above will, typically, result in a multiple definition error such you have seen. The reason is that, when compiling each source file, the compiler sees the declaration;
    int global_var;
as a definition. In other words, each object file (let's say function.o and main.o) will create something named global_var. The typical dumb linker, when it is given the two object files) sees this and sees something named global_var defined twice, and it can't decide which definition to use (typically the only think the dumb linker knows is that there are two things named global_var, but it doesn't know if their are the same type, let alone whether they are supposed to refer to the same thing).

This means we have to help the linker out so there is only one definition globally. That means we need to tell the compiler to do things differently. The formal description (in the C++ standard) is that we need to obey the "one definition rule" -- if anything is defined in more than one way within a complete program, we have undefined behaviour. In the above example, we have defined global_var twice, which intuitively might suggest we have violated something called the "one definition rule".

To fix the problem, we come back to the notion that a definition is a particular type of declaration. So we need to introduce a declaration that does not make the compiler generate one definition for each source file. The header file is the thing used by both source files, so let's have a look there.
 // header file named   global.h
    int global_var;
    int function();    // prototype for calling function()
This declaration of global_var is a definition. To change it so it is not a definition, we use the "extern" keyword.
 // header file named   global.h
    extern int global_var;
    int function();    // prototype for calling function()
Unfortunately, that is not enough. It means that each source file will see a declaration of global_var, but no definition. If we supply the object files to our dumb linker, it will complain about that (usually with something to the effect of undefined or unresolved symbols).

So we have to introduce one, and only one definition somewhere so the linker sees a definition. If we put such a definition into a header file, we're back where we started. So, we have to put it into one (and only one) source file. It doesn't matter where we put it, as long as it is in a source file that has #include'd our global.h. I'll put it into main.cpp
// main.cpp    (assume cpp is an extension for C++ files recognised by our compiler)

#include "global.h"

int global_var;     // our one and only definition

int main()
{
     // do things with global_var

     function();   // will do more things with global_var

     //  do even more things with global
}
Now, if we compile the two source files and provide them to a linker, it will see a reference to something named global_var in both object files. That tells it it needs to find a definition to hook those references to. It will find exactly one definition, within main.o. The linker is now happy, so it can build your program.

The only other comment is that the header file has what is called an include guard. It is a technique designed to prevent confusing the compiler should a header file be #included more than once. The name of the macro GLOBAL_H_INCLUDED needs to be chosen unique and distinct for every header file.
grumpy is offline   Reply With Quote