无法理解这里有什么问题: 我有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文件中的可以保护我免受这种链接问题的影响。
答案 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.h
和B.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.cpp
和k.cpp
:
// j.cpp
#include "a.h"
#include "b.h"
// j.cpp stuff
// k.cpp
#include "a.h"
#include "b.h"
// k.cpp stuff
此处包含警戒会阻止x.h
在j.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。