我喜欢帮助诊断我尝试使用g ++ 4.2.1编译时收到的重复符号错误的来源。
具体错误是
ld: duplicate symbol _SOCIODEM_FILENAMES in /var/folders/c+/c+eq1Qz1Feye7vxs5mQOUE+++TI/-Tmp-//ccP3yVgF.o and /var/folders/c+/c+eq1Qz1Feye7vxs5mQOUE+++TI/-Tmp-//cc1NqtRL.o
collect2: ld returned 1 exit status
仅当我在名为Parameters.h
的文件中包含此声明时才会出现错误:
// Parameters.h
#ifndef PARAMETERS_H
#define PARAMETERS_H
// ...[code snipped]...
const int NUM_SOCIODEM_FILES = 5;
const char * SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt",
"FLEDGE_PDF.txt",
"PAIR_PDF.txt",
"BIRTH_AGE_PDF.txt",
"SPLIT_PDF.txt" };
// ...[code snipped]...
#endif
我搜索了所有文件,这是唯一宣布SOCIODEM_FILENAMES
的地方。当我注释掉声明时,“重复符号”错误消失了。
我不熟悉链接器错误(如果这就是这个),并希望帮助解决问题。我的所有头文件都有#ifndef...#define...#endif
个包装器。我的编译命令是
g++ -o a.out -I /Applications/boost_1_42_0/ Host.cpp Simulation.cpp main.cpp Rdraws.cpp
提前致谢。
解决方案摘要
我现在在Parameters.h:
const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt",
"FLEDGE_PDF.txt",
"PAIR_PDF.txt",
"BIRTH_AGE_PDF.txt",
"SPLIT_PDF.txt" };
Parameters.h中的所有其他定义和声明都保持不变。安德烈和其他评论者总结了使用extern
的替代方法,这对我来说是过度的。
答案 0 :(得分:14)
由于某些原因,到目前为止,没有一个答案能够解释整数NUM_SOCIODEM_FILES
对象和数组SOCIODEM_FILENAMES
对象之间的区别。后者由于已解释的原因触发链接器错误:因为您将头文件包含到多个实现文件中。然而,前者会没有任何问题地链接(因为NUM_SOCIODEM_FILES
声明确实没有问题)。为什么呢?
原因是您的NUM_SOCIODEM_FILES
对象被声明为const
。在C ++中,const对象默认具有内部链接,这意味着即使它们在多个实现文件中定义,它们也不会导致链接问题。换句话说,在C ++中,NUM_SOCIODEM_FILES
等同于
static const int NUM_SOCIODEM_FILES = 5; /* internal linkage */
这就是为什么它不会导致任何链接问题。
同时您的SOCIODEM_FILENAMES
未声明为常量,这就是默认情况下它获取外部链接并最终导致链接器错误的原因。但是,如果您将SOCIODEM_FILENAMES
声明为const
,问题就会消失
const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = {
...
注意额外的const
放在声明中的位置。如果您只是添加额外的const
并保留其他所有内容(即保留头文件中SOCIODEM_FILENAMES
的定义),即使您将头文件包含在多个文件中,链接器也不会报告错误翻译单位。
这不是一种推荐的方法,因为这样你就会给你的SOCIODEM_FILENAMES
内部链接,并在每个翻译单元中最终得到SOCIODEM_FILENAMES
数组的独立副本 - 这可能会很好但是仍然没有多大意义。因此,对于您的数组,通常最好使用其他答案中建议的extern
方法。
但请注意,您通常不应该NUM_SOCIODEM_FILES
声明!它很好,在头文件中定义。除非你试图做一些不寻常的事情,否则标量常量通常应该在头文件中用初始化器定义 - 这样它们在所有翻译单元中都可以看作是编译时常量,这是一个相当有价值的要有的东西。所以,要注意在将NUM_SOCIODEM_FILES
的定义移到.cpp
文件中的其他一些答案中存在的奇怪建议 - 这实际上没有意义,并且完全错误。
答案 1 :(得分:7)
最有可能的是,您在多个源文件中#include
了这个文件。问题是每个包含都会导致名为SOCIODEM_FILENAMES
的变量的单独定义。包括警卫没有帮助。包含防护在单个编译单元中阻止多个声明;它们不会阻止多个编译单元中的多个定义。
您需要做的是在标头中将这些变量声明为extern
,然后在一个源文件中定义它们。 e.g。
// Parameters.h
#ifndef PARAMETERS_H
#define PARAMETERS_H
// ...[code snipped]...
extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
// ...[code snipped]...
#endif
然后:
// Parameters.cpp (or some other source file)
const int NUM_SOCIODEM_FILES = 5;
const char * SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt",
"FLEDGE_PDF.txt",
"PAIR_PDF.txt",
"BIRTH_AGE_PDF.txt",
"SPLIT_PDF.txt" };
你可以为int
不执行此操作,因为它是一个常量整数,因此编译器可以将其视为编译时常量,并且它永远不会出现在编译时码。但是,char*
不能以这种方式处理,因此必须只有一个定义(在C ++中称为“一个定义规则”)。
答案 2 :(得分:2)
标头保护(#ifndef..#endif
包装器)只是阻止您在单个源文件中多次包含相同的标头。您仍然可以拥有多个包含该标头的源文件,每个文件将分别声明该符号。由于它们都具有相同的名称,因此将这些源链接在一起将导致符号名称冲突。您可能希望在源文件中声明符号而不是头文件
答案 3 :(得分:2)
问题是你在头文件中放了一个定义。如果将该文件包含在多个编译单元(.cpp文件)中,您将生成多个定义,并且在链接时您将收到该错误。
您需要将这两个定义放在.cpp文件中,并将仅声明放在头文件中:
extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
答案 4 :(得分:2)
正如其他人所建议的那样,一种方法是将NUM_SOCIODEM_FILES
和SOCIODEM_FILENAMES
声明为extern
,并在外部文件中定义一次。另一种方法是将它们声明为static
- 这会导致它们在包含标头的每个目标文件中重复,但不会创建错误,因为该定义对该目标文件是私有的。您选择哪个选项完全取决于您自己的偏好。