I'd love help diagnosing the source of a duplicate symbol error that I'm receiving when I try to compile with g++ 4.2.1.
The specific error is
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
The error occurs only when I include this declaration in a file called 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
I've searched all my files, and this is the only place where SOCIODEM_FILENAMES
is declared. When I comment out the declaration, the 'duplicate symbol' error goes away.
I'm unfamiliar with linker errors (if that's what this is) and would appreciate help troubleshooting the problem. All my header files have #ifndef...#define...#endif
wrappers. My compile command is
g++ -o a.out -I /Applications/boost_1_42_0/ Host.cpp Simulation.cpp main.cpp Rdraws.cpp
Thanks in advance.
Solution summary
I now have in 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" };
All other definitions and declarations in Parameters.h are unchanged. Andrey and other commenters summarize an alternative approach using extern
, which is overkill for my purposes.
For some reason none of the answers so far cared to explain the difference between your integer NUM_SOCIODEM_FILES
object and array SOCIODEM_FILENAMES
object. The latter triggers the linker error for the reasons already explained: because you include you header file into multiple implementation files. Yet, the former would link without any problems (because there are indeed no problems with NUM_SOCIODEM_FILES
declaration). Why?
The reason for this is that your NUM_SOCIODEM_FILES
object is declared const
. In C++ const objects have internal linkage by default, meaning that they do not cause linking problems even if they are defined in multiple implementation files. In other words, in C++ your NUM_SOCIODEM_FILES
is equivalent to
static const int NUM_SOCIODEM_FILES = 5; /* internal linkage */
which is why it does not lead to any linking problems.
At the same time your SOCIODEM_FILENAMES
is not declared constant, which is why it gets external linkage by default and eventually leads to linker errors. But if you declare your SOCIODEM_FILENAMES
as const
as well, the problem will go away
const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = {
...
Note where the extra const
is placed in the declaration. If you just add that extra const
and leave everything else as is (i.e. keep the definition if SOCIODEM_FILENAMES
in the header file), the linker will not report the error even if you include your header file into multiple translation units.
This is not a recommended approach though, since that way you will give your SOCIODEM_FILENAMES
internal linkage and end up with a standalone copy of SOCIODEM_FILENAMES
array in each translation unit - something that might work fine but still makes very little sense. So, for your array, it is normally better to use the extern
approach recommended in other answers.
However, note that you shouldn't normally do it for NUM_SOCIODEM_FILES
declaration!!! It is fine as it is, defined in the header file. Unless you are trying to do something unusual, scalar constants should normally be defined with initializer in the header files - that way they can be seen as compile-time constants in all translation units, which is a rather valuable thing to have. So, beware of the strange advice present in some other answers to move the definition of NUM_SOCIODEM_FILES
into .cpp
file as well - this actually makes no sense and is a totally wrong thing to do.
Most likely, you are #include
ing this file in multiple source files. The problem is that each inclusion results in a separate definition for a variable named SOCIODEM_FILENAMES
. Include guards do not help with this. Include guards prevent multiple declarations within a single compilation unit; they do not prevent multiple definitions across several compilation units.
What you need to do is declare these variables as extern
in the header, and then define them in exactly one source file. 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
and then:
// 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" };
You can get away with not doing this for the int
because it is a constant integer, and so the compiler can just treat it as a compile-time constant, and it will never even show up in the compiled code. However, the char*
cannot be treated this way, and so must have exactly one definition (known as the "one definition rule" in C++).
The header guard (#ifndef..#endif
wrapper) just prevents you from including the same header multiple times in a single source file. You can still have multiple source files that include that header, and each one will declare that symbol separately. Since they all have the same name, linking those sources together will cause a symbol name collision. You probably want to declare the symbol in a source file instead of a header file
The problem is you are putting a definition in a header file. If you include that file in more than one compilation unit (.cpp file) you will be in effect creating multiple definitions and at link time you will get that error.
You need to put both those definitions in a .cpp file and put only a declaration in the header file:
extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With