我的项目中有三个文件。
交流
公元前
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的目标文件时将其用作共享库,我不会得到此错误。
谁能解释一下上面的错误给我,特别是在面对条件编译指令?
不能在头文件中声明变量。符号test_namespace::i同时被a.c和b.c导出。链接器找到了这两个,但不知道该使用哪个。
你想在test.h中做的是:
namespace test_namespace {
extern int i;
void f1();
}
,然后在中声明test_namespace::i。namespace test_namespace {
int i;
}
条件包含用于防止为同一源文件而不是为整个项目包含两次头文件。假设您有标题a.h
和b.h
,以及b.h
#include
和a.h
。然后,如果c.c
需要从两个头的东西,它#include
这两个。由于C预处理器使用文字文本替换,当它包含b.h
时,现在文件中将有两个#include "a.h"
指令,造成多个声明的混乱。(编辑:澄清你为什么在这种情况下遇到问题。)
Include守卫的作用是在编译单元的构建过程中保护多个头包含。当您有两个单独的代码文件和一个头文件时,就像您的示例一样,它们不是必需的。
(因此,当test.c
使用a.h
和b.h
时,在b.h
需要#包括a.h
的情况下,请考虑更多)
但是这是一个关于include守卫约定的注释,以及它在这种情况下如何没有给你带来任何东西。您遇到的具体技术问题(正如其他人指出的那样)是,您基本上在两个不同的目标文件中定义了相同的变量,当链接器将所有内容拉到一起时,它不知道您是想要a.o
还是b.o
中的变量。
(注意:虽然编译器通常可以设置为覆盖的东西,并使用namespace
这样的特性来构建c++代码,即使扩展名是.c
-你可能应该使用其他东西,如.cpp
: c++代码文件扩展名?
在header中定义test_namespace::i
。你可能需要的是头中的extern int i;
,并且定义在其中一个源文件。
当你说
int i;
实际上做了两件的事情:
1)声明符号i
2)在object文件
中为i
保留一些空间和一个符号技巧在于(1)应该只在每个文件中执行一次(实际上在这种情况下您可以重复它)——这就是为什么您有条件包含,您已经正确地完成了——但是(2)应该只在每个程序
中执行一次。解决方案是让头文件做
// Only declare -- don't create a symbol
extern int i;
在*.c文件中执行
int i;
头守卫(#ifndef
..)#define
. .#endif
)正在正常工作。a.c和b.c都包含test.h,因此它们都得到该头文件的副本。(当编译程序时,#include
所做的是将头文件的内容复制粘贴到源文件中。)
因为它们都有头文件的副本,所以它们都定义了变量test_namespace::i
。当链接器试图链接从a.c生成的代码和从b.c生成的代码时,它发现它们都定义了那个变量。它不知道该怎么做,所以它没有完成并输出一个错误。