与const char * []声明关联的重复符号错误

时间:2010-06-21 20:22:24

标签: c++ linker

我喜欢帮助诊断我尝试使用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的替代方法,这对我来说是过度的。

5 个答案:

答案 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_FILESSOCIODEM_FILENAMES声明为extern,并在外部文件中定义一次。另一种方法是将它们声明为static - 这会导致它们在包含标头的每个目标文件中重复,但不会创建错误,因为该定义对该目标文件是私有的。您选择哪个选项完全取决于您自己的偏好。