链接错误(#ifndef无法按预期工作)

时间:2012-03-23 06:46:24

标签: c++

无法理解这里有什么问题: 我有main.cpp文件,包括:

#include "lexan.h"
...

lexan.h文件:

#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_

在lexan2.h中:

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

问题是我收到链接错误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

我的错误在哪里?我以为

#ifndef _LEXAN_
#define _LEXAN_
lexan.h文件中的

可以保护我免受这种链接问题的影响。

3 个答案:

答案 0 :(得分:5)

您正在标题文件struct TokensList * tokens中创建lexan.h,然后在lexan.cpp和& lexan2.cpp违反 One Definition Rule(ODR)

请注意,标题保护会阻止在同一 Translation Unit 中包含相同的标题。在头文件中创建变量时,将在包含标题的每个转换单元中创建具有相同名称的变量的副本。这导致项目中链接器抱怨的多个相同的命名变量。

<强>解决方案:
如果您需要跨文件共享,则需要使用extern

如何使用extern
看看:
What are extern variables in C?
How to correctly use the extern keyword in c?

答案 1 :(得分:0)

您违反了一条定义规则,正如评论中暗示的那样。

头文件应该声明变量;他们应该(几乎)从不定义变量。您的标头定义了变量tokens,这意味着给定程序中只有一个源文件可以使用标头,这不是预期的。

我建议您查看What are extern variables in C?有关如何处理标题中变量的广泛(可能过于广泛)的讨论。

但基本规则是:

  • 不要在标题中定义变量;只有声明他们。

当然,这假设全局变量是必要的。如果可以,请避免它们。但是当它们是必要的时候,标题应该声明而不是定义变量。

答案 2 :(得分:0)

包含警戒只会阻止您处理相同的标题每个翻译单元(源文件)。

假设您有一个标题X.h,其中包含警卫:

// x.h
#ifndef X_H
#define X_H

    // x.h stuff

#endif

您还有A.hB.h,其中每个都包含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

然后我们有j.cppk.cpp

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

    // j.cpp stuff

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

    // k.cpp stuff

此处包含警戒会阻止x.hj.cpp内被处理两次,同样在k.cpp中。 x.h仍然包含两次,每个翻译单元一次。所以如果// x.h stuff是这样的话:

int myGlobalInt; // x.h stuff

虽然您阻止j.cpp(和k.cpp)两次定义此变量,但每个仍然定义了一次。在链接时,链接器会找到它们并抱怨。

你想要的是什么 - 除了避免全局变量,你应该尝试做 - 是这样的:

extern int myGlobalInt; // x.h stuff

现在每个翻译单元都会被告知“某个地方存在myGlobalInt”,您可以在您选择的单个翻译单元中明确定义它。

另外,不要使用那种头部防护装置;名称以下划线开头,后跟一个大写,以及以两个连续下划线开头的名称are reserved