我的项目中有三个文件。
交流转换器
b.c
test.h
test.h声明了一个
namespace test_namespace {
int i;
void f1();
};
test.h也被
包围#ifndef __x
#define __x
...
#endif
现在,a.c包含test.h,b.c也包含test.h。 a.c具有main()函数,b.c具有test_namespace :: f1()
的实现但是,在编译时,我收到链接错误 -
"test_namespace::i is already defined in <b.c's object file mapping in /tmp>"
如果我已经注意在test.h中包含条件编译预处理程序指令,为什么它包含在a.c和b.c这两个文件中?
另外值得注意的是,如果我将b.c单独编译为共享库,然后在链接a.c的目标文件时将其用作共享库,则不会出现此错误。
有人可以向我解释上述错误,特别是面对条件编译指令吗?
答案 0 :(得分:4)
您无法在头文件中声明变量。 符号test_namespace :: i由a.c和b.c导出。链接器找到两者并且不知道使用哪一个。
您在test.h中想要做的是:
namespace test_namespace {
extern int i;
void f1();
}
然后在 eather a.c或b.c中声明test_namespace :: i:
namespace test_namespace {
int i;
}
答案 1 :(得分:3)
条件包含用于防止标题被包含两次,因为相同的源文件不是整个项目。假设您有标题a.h
和b.h
以及b.h
#include
s a.h
。然后,如果c.c
需要两个标题中的内容,那么它们#include
。由于C预处理器使用文字文本替换,当它包含b.h
时,您的文件中现在会有两个#include "a.h"
指令,从而造成多个声明的破坏。 (编辑:澄清在这种情况下你遇到问题的原因。)
答案 2 :(得分:2)
包含防护用于保护编译单元构建中的多个标头包含。如果您有两个单独的代码文件和一个标题(例如您的示例),则不需要它们。
(在test.c
使用a.h
和b.h
的情况下,在b.h
需要#include a.h
的情况下,请多考虑一下。)
但这是关于包含守卫惯例的内容以及在这种情况下它是如何不给你买任何东西的注释。您遇到的具体技术问题(正如其他人指出的那样)是您基本上在两个不同的目标文件中定义相同的变量,并且当链接器将所有内容拉到一起时,它不知道您是否需要来自{的变量{1}}或a.o
。
(注意:虽然编译器通常可以设置为覆盖事物并使用b.o
等功能构建C ++代码,即使扩展名为namespace
- 您可能应该使用其他内容,例如.c
:C++ code file extension? .cc vs .cpp)
答案 3 :(得分:1)
您在标头中定义了test_namespace::i
。你可能是什么
want在标题中是extern int i;
,在其中一个中是定义
源文件。
答案 4 :(得分:1)
当你说
时发生了什么int i;
这实际上是两个的东西:
1)声明符号i
2)在目标文件
中为i
保留一些空格和符号
诀窍是(1)每个文件只应该执行一次(实际上你可以在这种情况下重复它) - 这就是为什么你有条件包含,你已经正确完成了 - 但是(2)应该每个程序只能执行一次
解决方案是让标题执行
// Only declare -- don't create a symbol
extern int i;
在* .c文件中执行
int i;
答案 5 :(得分:1)
标题保护(#ifndef
.. #define
.. #endif
)正在按预期工作。 a.c 和 b.c 都包含 test.h ,因此它们都获得该标头的副本。 (编译程序时,#include
的作用是在源文件中逐字复制粘贴标题的内容。)
由于它们都有标题的副本,因此它们都定义变量test_namespace::i
。当链接器尝试将从 a.c 生成的代码与从 b.c 生成的代码链接时,它会发现它们都定义了该变量。它不知道该怎么做,所以它不会完成并输出错误。