2010-06-21 13 views
7

me encantaría ayuda a diagnosticar la fuente de un error de símbolo duplicado que estoy recibiendo cuando intento compilar con g ++ 4.2.1.error de símbolo duplicado asociado con const char * [] declaración

el error específico

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 

El error se produce sólo cuando incluyo esta declaración en un archivo llamado 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 

He buscado en todos mis archivos, y este es el único lugar donde se declara SOCIODEM_FILENAMES. Cuando comento la declaración, el error del "símbolo duplicado" desaparece.

estoy familiarizado con errores de enlace (si eso es lo que es) y agradecería ayuda para solucionar el problema. Todos mis archivos de encabezado tienen envoltorios #ifndef...#define...#endif. Mi comando de compilación es

g++ -o a.out -I /Applications/boost_1_42_0/ Host.cpp Simulation.cpp main.cpp Rdraws.cpp 

Gracias de antemano.


Resumen Solución

ahora tengo en 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" }; 

Todas las demás definiciones y declaraciones en Parameters.h no se han modificado. Andrey y otros comentaristas resumen un enfoque alternativo usando extern, que es excesivo para mis propósitos.

+0

Genial. Sin embargo, todavía no entiendo por qué no declaras tu matriz 'SOCIODEM_FILENAMES' como' const'. ¿Se supone que las entradas de la matriz son modificables en tiempo de ejecución? – AnT

+0

@Andrey: Debería declararlos como constantes. Gracias. – Sarah

Respuesta

14

Por alguna razón, ninguna de las respuestas hasta ahora ha servido para explicar la diferencia entre su objeto entero NUM_SOCIODEM_FILES y el objeto de la matriz SOCIODEM_FILENAMES. Este último desencadena el error del enlazador por las razones ya explicadas: porque incluye su archivo de encabezado en varios archivos de implementación. Sin embargo, el primero se vincularía sin ningún problema (porque de hecho no hay problemas con la declaración NUM_SOCIODEM_FILES). ¿Por qué?

La razón de esto es que su objeto NUM_SOCIODEM_FILES está declarado const. En C++, los objetos const tienen el enlace interno de forma predeterminada, lo que significa que no causan problemas de vinculación incluso si están definidos en varios archivos de implementación. En otras palabras, en C++ su NUM_SOCIODEM_FILES es equivalente a

static const int NUM_SOCIODEM_FILES = 5; /* internal linkage */ 

por lo que no da lugar a ningún problema de enlace.

Al mismo tiempo que su SOCIODEM_FILENAMES no se ha declarado constante, por lo que se pone enlazado externo por defecto y, finalmente, conduce a errores ligador. Pero si usted declara su SOCIODEM_FILENAMES como const así, el problema va a desaparecer

const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { 
    ... 

Nota donde el extra const se coloca en la declaración. Si solo agrega ese extra const y deja todo lo demás tal como está (es decir, mantenga la definición SOCIODEM_FILENAMES en el archivo de encabezado), el enlazador no informará el error incluso si incluye el archivo de encabezado en varias unidades de traducción.

Esto no es un método recomendado embargo, ya que de esa manera se le dará a su enlace interno SOCIODEM_FILENAMES y terminan con una copia independiente de SOCIODEM_FILENAMES matriz en cada unidad de traducción - algo que podría funcionar bien, pero todavía tiene muy poco sentido. Por lo tanto, para su matriz, normalmente es mejor utilizar el enfoque extern recomendado en otras respuestas.

Sin embargo, tenga en cuenta que normalmente no debería hacerlo para la declaración NUM_SOCIODEM_FILES !!! Está bien tal como está, definido en el archivo de encabezado. A menos que intente hacer algo inusual, las constantes escalares normalmente deberían ser definidas con el inicializador en los archivos de encabezado, de esa manera se pueden ver como constantes de tiempo de compilación en todas las unidades de traducción, lo cual es algo bastante valioso.Por lo tanto, tenga cuidado con el extraño consejo presente en algunas otras respuestas para mover la definición de NUM_SOCIODEM_FILES al archivo .cpp, esto en realidad no tiene sentido y es una acción totalmente incorrecta.

+0

Wow, gracias. Esto explica mucho. Esto solucionó casi el mismo problema para mí. – goatlinks

2

La guardia de cabecera (#ifndef..#endif envoltorio) simplemente le impide incluyendo en el mismo encabezado varias veces en un solo archivo fuente. Todavía puede tener varios archivos fuente que incluyen ese encabezado, y cada uno declarará ese símbolo por separado. Dado que todos tienen el mismo nombre, vincular esas fuentes generará una colisión de nombre de símbolo. Es posible que desee declarar el símbolo en un archivo de origen en lugar de un archivo de cabecera

2

El problema es que están poniendo una definición en un fichero de cabecera. Si incluye ese archivo en más de una unidad de compilación (archivo .cpp), estará creando varias definiciones y, en el momento del enlace, obtendrá ese error.

Es necesario poner esas dos definiciones en un archivo .cpp y poner solamente una declaración en el archivo de cabecera:

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

Lo más probable es, usted es #include ing este archivo en varios archivos de origen. El problema es que cada inclusión da como resultado una definición separada para una variable llamada SOCIODEM_FILENAMES. Incluir guardias no ayuda con esto. Incluyen guardias impiden múltiples declaraciones dentro de una sola unidad de compilación; no impiden múltiples definiciones en varias unidades de compilación.

Lo que hay que hacer es declarar estas variables como extern en la cabecera, y luego definirlos exactamente en un archivo de origen. p.ej.

// Parameters.h 

#ifndef PARAMETERS_H 
#define PARAMETERS_H 

// ...[code snipped]... 
extern const int NUM_SOCIODEM_FILES; 
extern const char * SOCIODEM_FILENAMES[]; 
// ...[code snipped]... 
#endif 

y luego:

// 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" }; 

Usted puede conseguir lejos con no hacer esto para el int porque es un número entero constante, por lo que el compilador solo puede tratar como una constante en tiempo de compilación, y nunca aparecerá en el código compilado. Sin embargo, el char* no se puede tratar de esta manera, por lo que debe tener exactamente una definición (conocida como la "regla de una definición" en C++).

+0

¿Qué quiere decir con los parámetros int y double "nunca mostrar [ing] up en el código compilado"? Todos mis otros parámetros eran de tipo numérico constante y parecían estar presentes en el código compilado. – Sarah

+0

Quiero decir que el compilador no necesita necesariamente crear ubicaciones de memoria para almacenar estos números cuando el programa se está ejecutando. Esencialmente, el compilador puede tratarlo casi como si hubiera escrito '#define NUM_SOCIODEM_FILES 5' y simplemente reemplace la variable con el literal' 5' donde quiera que se use. Existe una regla de definición para que cada variable nombrada se pueda asignar a una única ubicación de memoria única. Si no se requiere una ubicación de memoria, entonces el problema es discutible. –

+2

Mover la definición de 'NUM_SOCIODEM_FILES' al archivo .cpp es realmente una mala idea. Los objetos escalares de Const normalmente se definen en archivos de encabezado. No hay nada de malo en eso y, de hecho, hay bastante en lo cierto con eso. En muchos casos, es extremadamente útil tener 'NUM_SOCIODEM_FILES' como expresión de constante integral. Esta respuesta destruyó esa valiosa propiedad de 'NUM_SOCIODEM_FILES' sin ningún motivo. – AnT

2

Como han sugerido otros, una forma de hacerlo es declarar NUM_SOCIODEM_FILES y SOCIODEM_FILENAMES como y definirlos una vez en un archivo externo. La otra forma es declararlos como static; esto hace que se dupliquen en cada archivo de objeto que incluye el encabezado, pero no creará un error ya que la definición es privada para ese archivo de objeto. La opción que elijas dependerá de tus preferencias.

+0

Gracias. No había sabido sobre esta opción. – Sarah

+2

Esto es peligroso si no comprende por completo lo que significa; está creando una instancia separada de esas variables en cada archivo de objeto. Probablemente esté bien (aunque sea ineficiente) en este caso, ya que son const, pero si hicieras esto con una variable mutable y lo cambiaras en un archivo, solo la instancia en ese archivo cambiaría –

+0

. No tenía planeado hacerlo. ruta por esa razón, pero no me di cuenta de que 'estático' crearía copias separadas para cada archivo. Aprecio la advertencia. – Sarah