maximus
maximus

Reputation: 4302

Linking error (#ifndef doesn't work as expected)

Can't understand what is a problem here: I have got main.cpp file where I am including:

#include "lexan.h"
...

The lexan.h file:

#ifndef _LEXAN_
#define _LEXAN_
enum Statements ...
//some function prototypes
...
struct TokensList {
    Statements statement;
    std::string value;
    struct TokensList *next;
};
struct TokensList *tokens = NULL;
#endif _LEXAN_

In lexan2.h:

#include "lexan.h"
// and some function prototypes

The problem is that I get the link error 2005:

1>lexan2.obj : error LNK2005: "struct TokensList * tokens" (?tokens@@3PAUTokensList@@A) already defined in lexan.obj
1>main.obj : error LNK2005: "struct TokensList * tokens" (?tokens@@3PAUTokensList@@A) already defined in lexan.obj

Where is my mistake? I thought the

#ifndef _LEXAN_
#define _LEXAN_

in the lexan.h file would protect me from such linking problems.

Upvotes: 1

Views: 1226

Answers (3)

Alok Save
Alok Save

Reputation: 206566

You are creating struct TokensList * tokens in the header file lexan.h and then including the header in both lexan.cpp & lexan2.cpp which violates the One Definition Rule(ODR).

Note that, header guards prevent including the same header in the same Translation Unit. When you create a variable in the header file, a copy of the variable with the same name gets created in every translation unit where you include the header. This leads to multiple same named variables in your project which the linker complains about.

Solution:
If you need to share it across files, you need to use extern.

How to use extern?
Have a look at:
What are extern variables in C?
How to correctly use the extern keyword in c?

Upvotes: 4

GManNickG
GManNickG

Reputation: 504063

Include guards only stop you from processing the same header twice per translation unit (source file).

Let's say you have a header, X.h, which has include guards:

// x.h
#ifndef X_H
#define X_H

    // x.h stuff

#endif

You also have A.h and B.h, each of which include X.h:

// a.h
#ifndef A_H
#define A_H

    #include "x.h"

    // a.h stuff

#endif

// b.h
#ifndef B_H
#define B_H

    #include "x.h"

    // b.h stuff

#endif

Then we have j.cpp and k.cpp:

// j.cpp
#include "a.h"
#include "b.h"

    // j.cpp stuff

// k.cpp
#include "a.h"
#include "b.h"

    // k.cpp stuff

Here the include guards prevent x.h from being processed twice within j.cpp, likewise in k.cpp. But x.h is still included twice, once for each translation unit. So if // x.h stuff was this:

int myGlobalInt; // x.h stuff

Although you prevent j.cpp (and k.cpp) from defining this variable twice, each still has defined it once. At link time, the linker finds them both and complains.

What you want — other than avoiding global variables, which you should try to do — is this:

extern int myGlobalInt; // x.h stuff

Now each translation unit gets told "there exists a myGlobalInt somewhere", and you can explicit define it in a single translation unit of your choosing.

Also, don't use those kinds of header guards; names beginning with an underscore followed by a capital, as well as those beginning with two consecutive underscores are reserved.

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 754470

You are violating the one definition rule, as hinted in comments.

Header files should declare variables; they should (almost) never define variables. Your header is defining the variable tokens, which means that only one source file in a given program can use the header, which is not what was intended.

I suggest taking a look at What are extern variables in C? for an extensive (possibly too extensive) discussion of how to handle variables in headers.

But the fundamental rule is:

  • Do not define variables in headers; only declare them.

Of course, that assumes that the global variable is necessary. When you can, avoid them. But when they are necessary, the header should declare, not define, the variable.

Upvotes: 0

Related Questions