【问题标题】:Duplicate symbol error associated with const char * [] declaration与 const char * [] 声明相关的重复符号错误
【发布时间】:2010-06-21 20:22:23
【问题描述】:

我很乐意帮助诊断我在尝试使用 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 中的所有其他定义和声明均未更改。 Andrey 和其他评论者总结了一种使用 extern 的替代方法,这对我的目的来说太过分了。

【问题讨论】:

  • 太棒了。但是我仍然不明白为什么您不将SOCIODEM_FILENAMES 数组声明为const。数组条目是否应该在运行时更改?
  • @Andrey:我应该将它们声明为常量。谢谢。

标签: c++ linker


【解决方案1】:

由于某种原因,到目前为止,没有一个答案愿意解释您的整数 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 文件中 - 这实际上没有任何意义,而且是完全错误的做法。

【讨论】:

  • 哇,谢谢。这解释了很多。这为我解决了几乎完全相同的问题。
【解决方案2】:

很可能,您在多个源文件中#includeing 这个文件。问题是每个包含都会导致一个名为SOCIODEM_FILENAMES 的变量的单独定义。包括警卫对此没有帮助。包含守卫可防止在单个编译单元中出现多个声明;它们不会阻止跨多个编译单元的多个定义

您需要做的是在标头中将这些变量声明为extern,然后将它们定义在一个源文件中。例如

// 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++ 中称为“一个定义规则”)。

【讨论】:

  • int 和 double 参数“甚至从未在已编译的代码中显示[ing]”是什么意思?我的所有其他参数都是常量数字类型,并且似乎存在于编译代码中。
  • 我的意思是编译器在程序执行时不一定需要创建内存位置来存储这些数字。本质上,编译器几乎可以将其视为您编写了#define NUM_SOCIODEM_FILES 5,只需将变量替换为文字5,无论它在哪里使用。存在一个定义规则,以便每个命名变量都可以映射到一个唯一的内存位置。如果不需要内存位置,则问题没有实际意义。
  • NUM_SOCIODEM_FILES 定义移动到.cpp 文件实际上是个坏主意。 const 标量对象通常在头文件中定义。这没有什么问题,实际上有很多正确的地方。在许多情况下,将NUM_SOCIODEM_FILES 作为一个整数常量表达式非常有用。这个答案无缘无故地破坏了NUM_SOCIODEM_FILES 的宝贵财产。
【解决方案3】:

标头保护(#ifndef..#endif 包装器)只是防止您在单个源文件中多次包含相同的标头。您仍然可以拥有多个包含该标头的源文件,并且每个源文件都将单独声明该符号。由于它们都具有相同的名称,将这些源链接在一起将导致符号名称冲突。您可能希望在源文件而不是头文件中声明符号

【讨论】:

    【解决方案4】:

    问题是您将定义放在头文件中。如果您将该文件包含在多个编译单元(.cpp 文件)中,您实际上将创建多个定义,并且在链接时您将收到该错误。

    您需要将这两个定义都放在一个 .cpp 文件中,并且在头文件中放置一个声明:

    extern const int NUM_SOCIODEM_FILES;
    extern const char * SOCIODEM_FILENAMES[];
    

    【讨论】:

      【解决方案5】:

      正如其他人所建议的,这样做的一种方法是将NUM_SOCIODEM_FILESSOCIODEM_FILENAMES 声明为extern 并在外部文件中定义它们一次。另一种方法是将它们声明为static--这会导致它们在包含标头的每个目标文件中重复,但不会产生错误,因为定义是该目标文件的私有。您选择哪个选项完全取决于您自己的喜好。

      【讨论】:

      • 谢谢。不知道这个选项。
      • 如果您不完全理解它的含义,这是很危险的——它会在每个目标文件中创建这些变量的单独实例。在这种情况下可能没问题(尽管效率低下),因为它们是 const,但是如果您使用可变变量执行此操作并在一个文件中更改它,则只有该文件中的实例会更改
      • 出于这个原因,我不打算走这条路,但我没有意识到“静态”会为每个文件创建单独的副本。我很感激这个警告。
      • @Sarah Static 表示符号对于包含它的目标文件是本地的。它“修复”了错误,因为现在您的所有源文件仍然具有相同名称的相同符号,但是这些符号是本地的而不是全局的,因此它们不会相互冲突
      猜你喜欢
      • 2014-09-27
      • 1970-01-01
      • 1970-01-01
      • 2020-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多