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

Sar*_*rah 7 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
Run Code Online (Sandbox Code Playgroud)

只有在我将此声明包含在名为的文件中时才会发生错误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
Run Code Online (Sandbox Code Playgroud)

我搜索了所有文件,这是唯一SOCIODEM_FILENAMES宣布的地方.当我注释掉声明时,"重复符号"错误消失了.

我不熟悉链接器错误(如果这就是这个),并希望帮助解决问题.我的所有头文件都有#ifndef...#define...#endif包装器.我的编译命令是

g++ -o a.out -I /Applications/boost_1_42_0/ Host.cpp Simulation.cpp main.cpp Rdraws.cpp
Run Code Online (Sandbox Code Playgroud)

提前致谢.


解决方案摘要

我现在在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"  };
Run Code Online (Sandbox Code Playgroud)

Parameters.h中的所有其他定义和声明都保持不变.安德烈和其他评论者总结了一种替代方法extern,这对我来说是过度的.

AnT*_*AnT 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 */
Run Code Online (Sandbox Code Playgroud)

这就是为什么它不会导致任何链接问题.

同时你SOCIODEM_FILENAMES的声明不是常量,这就是默认情况下它获得外部链接并最终导致链接器错误的原因.但如果你宣布你的SOCIODEM_FILENAMES同样const,问题就会消失

const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = {
  ...
Run Code Online (Sandbox Code Playgroud)

注意额外const的位置在声明中.如果您只是添加额外的内容const并保留其他所有内容(即SOCIODEM_FILENAMES在头文件中保留定义),即使您将头文件包含在多个翻译单元中,链接器也不会报告错误.

这不是一种推荐的方法,因为这样你就会给你的SOCIODEM_FILENAMES内部链接,并SOCIODEM_FILENAMES在每个翻译单元中最终得到一个独立的数组副本- 这可能工作正常,但仍然没有多大意义.因此,对于您的阵列,通常最好使用extern其他答案中建议的方法.

但请注意,您通常不应该NUM_SOCIODEM_FILES申报!!! 它很好,在头文件中定义.除非你试图做一些不寻常的事情,否则通常应该在头文件中使用初始化程序定义标量常量- 这样它们可以被视为所有翻译单元中的编译时常量,这是一个相当有价值的东西.所以,要注意一些其他答案中存在的奇怪建议,也就是将文件的定义NUM_SOCIODEM_FILES移到.cpp文件中 - 这实际上没有任何意义,这是完全错误的事情.


Tyl*_*nry 7

最有可能的是,您#include将此文件存储在多个源文件中.问题是每个包含都会导致名为变量的单独定义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
Run Code Online (Sandbox Code Playgroud)

然后:

// 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"  };
Run Code Online (Sandbox Code Playgroud)

你可以逃避不这样做,int因为它是一个常量整数,所以编译器可以把它当作编译时常量,它永远不会出现在编译代码中.但是,char*不能这样处理,因此必须有一个定义(在C++中称为"一个定义规则").

  • 将`NUM_SOCIODEM_FILES`定义移动到.cpp文件实际上是一个坏主意.Const标量对象通常在头文件中定义.这没有什么不对,实际上有很多正确的.在许多情况下,将"NUM_SOCIODEM_FILES"作为整数常量表达式非常有用.这个答案毫无理由地破坏了"NUM_SOCIODEM_FILES"的宝贵财产. (2认同)