我只是构建一个简单的C ++项目。代码如下所示:
-------- head.h --------
#ifndef _HEAD_H_
#define _HEAD_H_
int my_var = 100;
#endif
-------- src1.cpp --------
#include "head.h"
-------- src2.cpp --------
#include "head.h"
int main() { return 0; }
我使用这些命令构建二进制文件:
g++ -c src1.cpp -o scr1.o
g++ -c src2.cpp -o src2.o
g++ -o a.out src2.o src1.o
但它在链接步骤失败并且告诉我我有“my_var”的多重定义。我不是只在头文件中使用安全保护宏吗?我不明白。有人知道为什么吗?
感谢。
PS: 如果我将my_var定义为一个静态变量,那么代码可以传递链接。我也不明白。
答案 0 :(得分:3)
按以下方式更改标题
#ifndef _HEAD_H_
#define _HEAD_H_
extern int my_var;
#endif
例如,在带有main
的模块中添加行#include "head.h"
int my_var = 100;
int main() { return 0; }
问题在于,由于标头包含在两个模块中,因此每个模块包含一个带有外部链接的变量,其名称与其他模块中的变量相同。并且链接器不知道要使用哪个变量。
答案 1 :(得分:2)
您每个编译单元一次定义my_var
。请记住,包括警卫在每个编译单元的基础上运行。
要解决此问题,您应该在标题中声明 my_var
为extern
:
#ifndef _HEAD_H_
#define _HEAD_H_
extern int my_var;
#endif
使用在其中一个源文件中和定义
int my_var = 100;
然后链接器只能看到一个定义,一切都会好的。
答案 2 :(得分:1)
防护仅阻止标头在同一编译单元中被包含两次。
汇编src1.o
和src2.o
时,每个人都会包含my_var
的定义。当您将它们链接到创建a.out
时,编译器无法合并这些定义(即使它们是相同的),也会失败。
您要做的是将my_var
声明为extern:
---- head.h ----
#ifndef _HEAD_H_
#define _HEAD_H_
extern int my_var;
#endif
---- my_var.c ----
#include "head.h"
int my_var = 100;
然后编译所有源文件:
g++ -c my_var.cpp -o my_var.o
g++ -c src1.cpp -o scr1.o
g++ -c src2.cpp -o src2.o
g++ -o a.out my_var.o src2.o src1.o
这样,my_var
将在每个文件中声明,但只会在my_var.o
中定义。这里有一个重要的区别。尝试省略链接my_var.o
,您将看到编译器要说的内容:)
答案 3 :(得分:0)
你的问题归结为理解C ++存储类;静态和外部更准确。
extern says: this variable is defined in a different file, this
is only a declaration
static says: this variable's lifetime is the program lifetime and
restrict it's visibility to the current file.
What happens in your case:
- each file includes the variable declaration and definition `int my_var = 100;`,
so both object scr1.o and scr2.o will contain the variable definition. When
linking them, the linker cannot solve the ambiguity and throws an error.
- adding the extern qualifier and defining the variable in one of the files
solves this problem: in one source it'll be an external unresolved at compile
time, symbol and the other will contain the actual symbol.
关于你问题的第二部分;为什么添加静态似乎有助于链接器。看看我刚才所说的静态存储类:基本上每个对象都有自己的变量副本 - 不一样!