¿Qué son los errores de símbolo externo de referencia indefinida / sin resolver? ¿Cuáles son las causas comunes y cómo solucionarlas / prevenirlas?
Siéntase libre de editar / agregar el suyo.
c++
linker-errors
undefined-reference
c++-faq
unresolved-external
Luchian Grigore
fuente
fuente
Respuestas:
La compilación de un programa C ++ se lleva a cabo en varios pasos, como se especifica en 2.2 (créditos a Keith Thompson para la referencia) :
Los errores especificados ocurren durante esta última etapa de compilación, más comúnmente conocida como vinculación. Básicamente significa que compiló un montón de archivos de implementación en archivos de objetos o bibliotecas y ahora desea que funcionen juntos.
Digamos que definiste el símbolo
a
ena.cpp
. Ahora,b.cpp
declaró ese símbolo y lo usó. Antes de vincular, simplemente supone que ese símbolo se definió en algún lugar , pero aún no le importa dónde. La fase de vinculación es responsable de encontrar el símbolo y vincularlo correctamenteb.cpp
(bueno, en realidad al objeto o biblioteca que lo usa).Si está utilizando Microsoft Visual Studio, verá que los proyectos generan
.lib
archivos. Contienen una tabla de símbolos exportados y una tabla de símbolos importados. Los símbolos importados se resuelven en las bibliotecas con las que se vincula y los símbolos exportados se proporcionan para las bibliotecas que usan ese.lib
(si corresponde).Existen mecanismos similares para otros compiladores / plataformas.
Mensajes de error son comunes
error LNK2001
,error LNK1120
,error LNK2019
para Microsoft Visual Studio yundefined reference to
symbolName de GCC .El código:
generará los siguientes errores con GCC :
y errores similares con Microsoft Visual Studio :
Las causas comunes incluyen:
#pragma
(Microsoft Visual Studio)UNICODE
Definiciones inconsistentesfuente
Miembros de la clase:
Un
virtual
destructor puro necesita una implementación.Declarar un destructor puro aún requiere que lo defina (a diferencia de una función normal):
Esto sucede porque los destructores de clase base se llaman cuando el objeto se destruye implícitamente, por lo que se requiere una definición.
virtual
los métodos deben implementarse o definirse como puros.Esto es similar a los no
virtual
métodos sin definición, con el razonamiento adicional de que la declaración pura genera una vtable ficticia y puede obtener el error del vinculador sin usar la función:Para que esto funcione, declare
X::foo()
como puro:virtual
Miembros no pertenecientes a la claseAlgunos miembros deben definirse incluso si no se usan explícitamente:
Lo siguiente produciría el error:
La implementación puede ser en línea, en la propia definición de clase:
o afuera:
Si la implementación está fuera de la definición de clase, pero en un encabezado, los métodos deben marcarse
inline
para evitar una definición múltiple.Todos los métodos de miembros utilizados deben definirse si se usan.
Un error común es olvidar calificar el nombre:
La definición debería ser
static
los miembros de datos deben definirse fuera de la clase en una sola unidad de traducción :Se puede proporcionar un inicializador para un
static
const
miembro de datos de tipo integral o de enumeración dentro de la definición de clase; sin embargo, odr-use de este miembro aún requerirá una definición del alcance del espacio de nombres como se describió anteriormente. C ++ 11 permite la inicialización dentro de la clase para todosstatic const
los miembros de datos.fuente
Error al vincular con bibliotecas / archivos de objetos apropiados o compilar archivos de implementación
Comúnmente, cada unidad de traducción generará un archivo de objeto que contiene las definiciones de los símbolos definidos en esa unidad de traducción. Para usar esos símbolos, debe vincular con esos archivos de objetos.
En gcc , debe especificar todos los archivos de objetos que se vincularán en la línea de comandos, o compilar los archivos de implementación juntos.
El
libraryName
aquí es sólo el nombre dado testimonio de la biblioteca, sin adiciones específicas de la plataforma. Entonces, por ejemplo, en Linux, los archivos de la biblioteca generalmente se llamanlibfoo.so
pero solo escribiría-lfoo
. En Windows ese mismo archivo podría ser llamadofoo.lib
, pero usaría el mismo argumento. Es posible que deba agregar el directorio donde se pueden encontrar esos archivos-L‹directory›
. Asegúrese de no escribir un espacio después-l
o-L
.Para XCode : agregue las rutas de búsqueda de encabezado de usuario -> agregue la ruta de búsqueda de biblioteca -> arrastre y suelte la referencia de biblioteca real en la carpeta del proyecto.
Bajo MSVS , los archivos agregados a un proyecto automáticamente tienen sus archivos de objeto vinculados entre sí y
lib
se generaría un archivo (en uso común). Para usar los símbolos en un proyecto separado, debe incluir loslib
archivos en la configuración del proyecto. Esto se hace en la sección Linker de las propiedades del proyecto, enInput -> Additional Dependencies
. (la ruta allib
archivo debe agregarseLinker -> General -> Additional Library Directories
) Cuando se usa una biblioteca de terceros que se proporciona con unlib
archivo, el no hacerlo generalmente genera el error.También puede suceder que olvide agregar el archivo a la compilación, en cuyo caso no se generará el archivo objeto. En gcc agregarías los archivos a la línea de comando. En MSVS, agregar el archivo al proyecto hará que lo compile automáticamente (aunque los archivos pueden, manualmente, excluirse individualmente de la compilación).
En la programación de Windows, el signo revelador de que no vinculó una biblioteca necesaria es que el nombre del símbolo no resuelto comienza con
__imp_
. Busque el nombre de la función en la documentación, y debería decir qué biblioteca necesita usar. Por ejemplo, MSDN coloca la información en un cuadro en la parte inferior de cada función en una sección llamada "Biblioteca".fuente
gcc main.c
lugar degcc main.c other.c
(lo que los principiantes suelen hacer antes de que sus proyectos sean tan grandes como para construir archivos .o).Declarado pero no definió una variable o función.
Una declaración variable típica es
Como esto es solo una declaración, se necesita una sola definición . Una definición correspondiente sería:
Por ejemplo, lo siguiente generaría un error:
Observaciones similares se aplican a las funciones. Declarar una función sin definirla conduce al error:
Tenga cuidado de que la función que implemente coincida exactamente con la que declaró. Por ejemplo, puede tener calificadores cv no coincidentes:
Otros ejemplos de desajustes incluyen
El mensaje de error del compilador a menudo le dará la declaración completa de la variable o función que se declaró pero nunca se definió. Compárelo estrechamente con la definición que proporcionó. Asegúrese de que cada detalle coincida.
fuente
#includes
no agregado al directorio de origen también se incluyen en la categoría de definiciones faltantes.El orden en que se especifican las bibliotecas vinculadas interdependientes es incorrecto.
El orden en que las bibliotecas están vinculadas importa si las bibliotecas dependen unas de otras. En general, si la biblioteca
A
depende de la bibliotecaB
,libA
DEBE aparecer anteslibB
en las banderas del vinculador.Por ejemplo:
Crea las bibliotecas:
Compilar:
Así que para repetir de nuevo, el orden HACE importa!
fuente
¿Qué es una "referencia indefinida / símbolo externo no resuelto"?
Trataré de explicar qué es una "referencia indefinida / símbolo externo no resuelto".
Por ejemplo tenemos un código
y
Hacer archivos de objetos
Después de la fase de ensamblador tenemos un archivo de objeto, que contiene cualquier símbolo para exportar. Mira los símbolos
He rechazado algunas líneas de salida, porque no importan
Entonces, vemos los siguientes símbolos para exportar.
src2.cpp no exporta nada y no hemos visto sus símbolos
Vincula nuestros archivos de objetos
y ejecutarlo
Linker ve símbolos exportados y lo vincula. Ahora intentamos descomentar líneas en src2.cpp como aquí
y reconstruir un archivo objeto
OK (sin errores), porque solo construimos archivos de objetos, la vinculación aún no se ha realizado. Intenta vincular
Ha sucedido porque nuestro local_var_name es estático, es decir, no es visible para otros módulos. Ahora más profundamente. Obtenga la salida de la fase de traducción
Entonces, hemos visto que no hay una etiqueta para local_var_name, es por eso que linker no la ha encontrado. Pero somos piratas informáticos :) y podemos solucionarlo. Abra src1.s en su editor de texto y cambie
a
es decir, deberías tener como abajo
hemos cambiado la visibilidad de local_var_name y establecemos su valor en 456789. Intenta construir un archivo de objeto a partir de él
ok, ver salida de readelf (símbolos)
ahora local_var_name tiene Bind GLOBAL (was LOCAL)
enlace
y ejecutarlo
ok, lo hackeamos :)
Por lo tanto, como resultado, se produce un "error de símbolo externo de referencia indefinido / no resuelto" cuando el vinculador no puede encontrar símbolos globales en los archivos de objetos.
fuente
Los símbolos se definieron en un programa en C y se usaron en código C ++.
La función (o variable)
void foo()
se definió en un programa en C e intenta usarla en un programa en C ++:El vinculador de C ++ espera que los nombres se alteren, por lo que debe declarar la función como:
De manera equivalente, en lugar de definirse en un programa en C, la función (o variable)
void foo()
se definió en C ++ pero con enlace C:e intenta usarlo en un programa C ++ con enlace C ++.
Si se incluye una biblioteca completa en un archivo de encabezado (y se compiló como código C); la inclusión deberá ser la siguiente;
fuente
#ifdef __cplusplus [\n] extern"C" { [\n] #endif
y#ifdef __cplusplus [\n] } [\n] #endif
(al[\n]
ser un retorno de carro real, pero no puedo escribir esto correctamente en el comentario).extern "C" { #include <myCppHeader.h> }
.Si todo lo demás falla, vuelva a compilar.
Recientemente pude deshacerme de un error externo no resuelto en Visual Studio 2012 simplemente volviendo a compilar el archivo ofensivo. Cuando reconstruí, el error desapareció.
Esto generalmente ocurre cuando dos (o más) bibliotecas tienen una dependencia cíclica. La biblioteca A intenta usar símbolos en B.lib y la biblioteca B intenta usar símbolos de A.lib. Tampoco existen para empezar. Cuando intentas compilar A, el paso de enlace fallará porque no puede encontrar B.lib. A.lib será generado, pero no dll. Luego compila B, que tendrá éxito y generará B.lib. Re-compilar A ahora funcionará porque ahora se encuentra B.lib.
fuente
Importar / exportar incorrectamente métodos / clases a través de módulos / dll (específico del compilador).
MSVS requiere que especifique qué símbolos exportar e importar usando
__declspec(dllexport)
y__declspec(dllimport)
.Esta doble funcionalidad generalmente se obtiene mediante el uso de una macro:
La macro
THIS_MODULE
solo se definiría en el módulo que exporta la función. De esa manera, la declaración:se expande a
y le dice al compilador que exporte la función, ya que el módulo actual contiene su definición. Al incluir la declaración en un módulo diferente, se expandiría a
y le dice al compilador que la definición está en una de las bibliotecas con las que se vinculó (ver también 1) ).
Puedes importar / exportar clases similares:
fuente
visibility
los.def
archivos de GCC y Windows , ya que estos también influyen en el nombre y la presencia del símbolo..def
archivos en años. Siéntase libre de agregar una respuesta o editar esta.Este es uno de los mensajes de error más confusos que todos los programadores de VC ++ han visto una y otra vez. Hagamos las cosas claras primero.
A. ¿Qué es el símbolo? En resumen, un símbolo es un nombre. Puede ser un nombre de variable, un nombre de función, un nombre de clase, un nombre de typedef, o cualquier cosa, excepto los nombres y signos que pertenecen al lenguaje C ++. Es definido por el usuario o introducido por una biblioteca de dependencias (otra definida por el usuario).
B. ¿Qué es externo? En VC ++, cada archivo fuente (.cpp, .c, etc.) se considera como una unidad de traducción, el compilador compila una unidad a la vez y genera un archivo de objeto (.obj) para la unidad de traducción actual. (Tenga en cuenta que cada archivo de encabezado incluido en este archivo fuente se procesará previamente y se considerará como parte de esta unidad de traducción) Todo lo que se encuentra dentro de una unidad de traducción se considera interno, todo lo demás se considera externo. En C ++, puede hacer referencia a un símbolo externo utilizando palabras clave como
extern
,__declspec (dllimport)
etc.C. ¿Qué es "resolver"? Resolver es un término de tiempo de enlace. En el tiempo de enlace, el enlazador intenta encontrar la definición externa para cada símbolo en los archivos de objetos que no pueden encontrar su definición internamente. El alcance de este proceso de búsqueda incluye:
Este proceso de búsqueda se llama resolver.
D. Finalmente, ¿por qué símbolo externo no resuelto? Si el vinculador no puede encontrar la definición externa de un símbolo que no tiene una definición interna, informa un error de símbolo externo no resuelto.
E. Posibles causas de LNK2019 : error de símbolo externo no resuelto. Ya sabemos que este error se debe a que el enlazador no pudo encontrar la definición de símbolos externos, las posibles causas pueden clasificarse como:
Por ejemplo, si tenemos una función llamada foo definida en a.cpp:
En b.cpp queremos llamar a la función foo, así que agregamos
para declarar la función foo () y llamarla en otro cuerpo de función, diga
bar()
:Ahora, cuando cree este código, recibirá un error LNK2019 quejándose de que foo es un símbolo no resuelto. En este caso, sabemos que foo () tiene su definición en a.cpp, pero diferente de la que estamos llamando (valor de retorno diferente). Este es el caso de que exista definición.
Si queremos llamar a algunas funciones en una biblioteca, pero la biblioteca de importación no se agrega a la lista de dependencias adicional (establecida desde:)
Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
de la configuración de su proyecto. Ahora el enlazador reportará un LNK2019 ya que la definición no existe en el alcance de búsqueda actual.fuente
Implementaciones de plantillas no visibles.
Las plantillas no especializadas deben tener sus definiciones visibles para todas las unidades de traducción que las usan. Eso significa que no puede separar la definición de una plantilla a un archivo de implementación. Si debe separar la implementación, la solución habitual es tener un
impl
archivo que incluya al final del encabezado que declara la plantilla. Una situación común es:Para solucionar esto, debe mover la definición de
X::foo
al archivo de encabezado o algún lugar visible para la unidad de traducción que lo utiliza.Las plantillas especializadas pueden implementarse en un archivo de implementación y la implementación no tiene que ser visible, pero la especialización debe declararse previamente.
Para más explicaciones y otra posible solución (instanciación explícita) vea esta pregunta y respuesta .
fuente
referencia indefinida
WinMain@16
o referencia de punto de entrada 'inusual' similarmain()
(especialmente paraestudio visual)Es posible que no haya elegido el tipo de proyecto correcto con su IDE real. El IDE puede querer vincular, por ejemplo, proyectos de aplicaciones de Windows a dicha función de punto de entrada (como se especifica en la referencia que falta más arriba), en lugar de la
int main(int argc, char** argv);
firma comúnmente utilizada .Si su IDE admite proyectos de consola simple, es posible que desee elegir este tipo de proyecto, en lugar de un proyecto de aplicación de Windows.
Aquí están case1 y case2 manejados con más detalle a partir de un mundo real problema.
fuente
WinMain
. Los programas válidos de C ++ necesitan amain
.Además, si está utilizando bibliotecas de terceros, asegúrese de tener los binarios correctos de 32/64 bits
fuente
Microsoft ofrece una
#pragma
referencia de la biblioteca correcta en el momento del enlace;Además de la ruta de la biblioteca, incluido el directorio de la biblioteca, este debe ser el nombre completo de la biblioteca.
fuente
El paquete Visual Studio NuGet debe actualizarse para la nueva versión del conjunto de herramientas
Acabo de tener este problema al intentar vincular libpng con Visual Studio 2013. El problema es que el archivo del paquete solo tenía bibliotecas para Visual Studio 2010 y 2012.
La solución correcta es esperar que el desarrollador lance un paquete actualizado y luego actualice, pero funcionó para mí pirateando una configuración adicional para VS2013, apuntando a los archivos de la biblioteca VS2012.
Edité el paquete (en la
packages
carpeta dentro del directorio de la solución) buscandopackagename\build\native\packagename.targets
y dentro de ese archivo, copiando todas lasv110
secciones. Cambiév110
av120
en los campos de condición solo teniendo mucho cuidado de dejar todas las rutas de archivo comov110
. Esto simplemente permitió que Visual Studio 2013 se vincule a las bibliotecas para 2012, y en este caso, funcionó.fuente
Supongamos que tiene un gran proyecto escrito en c ++ que tiene miles de archivos .cpp y miles de archivos .h. Y digamos que el proyecto también depende de diez bibliotecas estáticas. Digamos que estamos en Windows y construimos nuestro proyecto en Visual Studio 20xx. Cuando presiona Ctrl + F7 Visual Studio para comenzar a compilar la solución completa (supongamos que solo tenemos un proyecto en la solución)
¿Cuál es el significado de la compilación?
El segundo paso de la compilación lo realiza Linker.Linker debería fusionar todo el archivo de objeto y finalmente construir la salida (que puede ser un archivo ejecutable o una biblioteca)
Pasos para vincular un proyecto
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
Observación
Cómo resolver este tipo de error
Error de tiempo del compilador:
Error de tiempo del vinculador
#pragma once
para permitir que el compilador no incluya un encabezado si ya estaba incluido en el .cpp actual que se compilafuente
Un error en el compilador / IDE
Recientemente tuve este problema, y resultó que era un error en Visual Studio Express 2013 . Tuve que eliminar un archivo fuente del proyecto y volver a agregarlo para superar el error.
Pasos para probar si cree que podría ser un error en el compilador / IDE:
fuente
Use el enlazador para ayudar a diagnosticar el error
La mayoría de los enlazadores modernos incluyen una opción detallada que se imprime en diversos grados;
Para gcc y clang; normalmente agregaría
-v -Wl,--verbose
o-v -Wl,-v
a la línea de comando. Más detalles se pueden encontrar aquí;Para MSVC,
/VERBOSE
(en particular/VERBOSE:LIB
) se agrega a la línea de comando de enlace./VERBOSE
opción del vinculador .fuente
El archivo .lib vinculado está asociado a un .dll
Tuve el mismo problema. Digamos que tengo proyectos MyProject y TestProject. Había vinculado efectivamente el archivo lib de MyProject al TestProject. Sin embargo, este archivo lib se produjo cuando se creó la DLL para MyProject. Además, no contenía código fuente para todos los métodos en MyProject, sino solo acceso a los puntos de entrada de la DLL.
Para resolver el problema, construí MyProject como LIB y vinculé TestProject a este archivo .lib (copio y pego el archivo .lib generado en la carpeta TestProject). Entonces puedo construir nuevamente MyProject como una DLL. Se está compilando ya que la biblioteca a la que está vinculado TestProject contiene código para todos los métodos en las clases de MyProject.
fuente
Dado que las personas parecen estar dirigidas a esta pregunta cuando se trata de errores de enlazador, voy a agregar esto aquí.
Una posible razón para los errores de enlazador con GCC 5.2.0 es que ahora se elige una nueva biblioteca ABI de libstdc ++ por defecto.
Entonces, si de repente obtiene errores de enlazador al cambiar a un GCC después de 5.1.0, esto sería algo que debe verificar.
fuente
Un contenedor alrededor de GNU ld que no admite scripts de enlazador
Algunos archivos .so son realmente scripts de enlazador GNU ld , por ejemplo, el archivo libtbb.so es un archivo de texto ASCII con este contenido:
Es posible que algunas compilaciones más complejas no admitan esto. Por ejemplo, si incluye -v en las opciones del compilador, puede ver que mainwin gcc wrapper mwdip descarta los archivos de comando de script de enlazador en la lista de salida detallada de las bibliotecas para vincular. Una solución simple es reemplazar el comando de entrada de script de enlazador archivo con una copia del archivo en su lugar (o un enlace simbólico), por ejemplo
O puede reemplazar el argumento -l con la ruta completa de .so, por ejemplo, en lugar de
-ltbb
do/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
fuente
Su enlace consume bibliotecas antes que los archivos de objeto que se refieren a ellas
libfoo
dependelibbar
, entonces su enlace se pone correctamentelibfoo
anteslibbar
.undefined reference to
algo errores.#include
dy son, de hecho se define en las bibliotecas que está enlazado.Los ejemplos están en C. También podrían ser C ++
Un ejemplo mínimo de una biblioteca estática que usted mismo creó.
my_lib.c
my_lib.h
eg1.c
Usted construye su biblioteca estática:
Usted compila su programa:
Intenta vincularlo
libmy_lib.a
y falla:El mismo resultado si compila y vincula en un solo paso, como:
Un ejemplo mínimo que involucra una biblioteca de sistema compartida, la biblioteca de compresión
libz
eg2.c
Compila tu programa:
Intenta vincular tu programa
libz
y falla:Lo mismo si compila y enlaza de una vez:
Y una variación en el ejemplo 2 que involucra
pkg-config
:¿Que estas haciendo mal?
En la secuencia de archivos de objetos y bibliotecas que desea vincular para hacer su programa, está colocando las bibliotecas antes de los archivos de objetos que se refieren a ellas. Necesita colocar las bibliotecas después de los archivos de objeto que se refieren a ellas.
Ejemplo de enlace 1 correctamente:
Éxito:
Ejemplo de enlace 2 correctamente:
Éxito:
Enlace la
pkg-config
variación del ejemplo 2 correctamente:La explicación
La lectura es opcional de aquí en adelante .
Por defecto, un comando de enlace generado por GCC, en su distribución, consume los archivos en el enlace de izquierda a derecha en la secuencia de la línea de comandos. Cuando encuentra que un archivo se refiere a algo y no contiene una definición para él, buscará una definición en los archivos más a la derecha. Si finalmente encuentra una definición, la referencia se resuelve. Si alguna referencia permanece sin resolver al final, el enlace falla: el enlazador no busca hacia atrás.
Primero, ejemplo 1 , con biblioteca estática
my_lib.a
Una biblioteca estática es un archivo indexado de archivos de objetos. Cuando el vinculador encuentra
-lmy_lib
en la secuencia de vinculación y descubre que esto se refiere a la biblioteca estática./libmy_lib.a
, quiere saber si su programa necesita alguno de los archivos de objetolibmy_lib.a
.Solo hay un archivo de objeto
libmy_lib.a
, es decirmy_lib.o
, y solo hay una cosa definidamy_lib.o
, es decir, la funciónhw
.El vinculador decidirá que su programa necesita
my_lib.o
si y solo si ya sabe que su programa hace referenciahw
, en uno o más de los archivos de objetos que ya ha agregado al programa, y que ninguno de los archivos de objetos que ya ha agregado contiene un definición parahw
.Si eso es cierto, entonces el enlazador extraerá una copia de
my_lib.o
la biblioteca y la agregará a su programa. Luego, su programa contiene una definición parahw
, por lo que sus referencias ahw
se resuelven .Cuando intentas vincular el programa como:
el enlazador no se ha agregado
eg1.o
al programa cuando ve-lmy_lib
. Porque en ese punto, no ha vistoeg1.o
. Su programa aún no hace ninguna referencia ahw
: todavía no hace ninguna referencia en absoluto , porque todas las referencias que hace están eneg1.o
.Por lo tanto, el enlazador no se agrega
my_lib.o
al programa y no tiene más uso paralibmy_lib.a
.Luego, lo encuentra
eg1.o
y lo agrega como programa. Un archivo de objeto en la secuencia de enlace siempre se agrega al programa. Ahora, el programa hace referencia ahw
, y no contiene una definición dehw
; pero no queda nada en la secuencia de enlace que pueda proporcionar la definición que falta. La referencia ahw
termina sin resolver y el enlace falla.Segundo, ejemplo 2 , con biblioteca compartida
libz
Una biblioteca compartida no es un archivo de archivos de objetos ni nada parecido. Es mucho más parecido a un programa que no tiene una
main
función y, en cambio, expone varios otros símbolos que define, para que otros programas puedan usarlos en tiempo de ejecución.Configurar hoy en día muchas distribuciones de Linux GCC su cadena de herramientas para que sus conductores de idiomas (
gcc
,g++
,gfortran
etc) instruir a los enlazador sistema (ld
) a las bibliotecas compartidas de enlace de un a demanda base. Tienes una de esas distribuciones.Esto significa que cuando el enlazador encuentra
-lz
en la secuencia de enlace, y descubre que esto se refiere a la biblioteca compartida (por ejemplo)/usr/lib/x86_64-linux-gnu/libz.so
, quiere saber si las referencias que ha agregado a su programa que aún no están definidas tienen definiciones que son exportado porlibz
Si eso es cierto, entonces el enlazador no copiará ningún fragmento
libz
ni lo agregará a su programa; en su lugar, solo buscará el código de su programa para que:En tiempo de ejecución, el cargador de programas del sistema cargará una copia
libz
en el mismo proceso que su programa cada vez que cargue una copia de su programa, para ejecutarlo.En tiempo de ejecución, cada vez que su programa hace referencia a algo definido
libz
, esa referencia utiliza la definición exportada por la copia delibz
en el mismo proceso.Su programa quiere referirse solo a una cosa que tiene una definición exportada por
libz
, a saber, la funciónzlibVersion
, a la que se hace referencia solo una vez, eneg2.c
. Si el vinculador agrega esa referencia a su programa y luego encuentra la definición exportada porlibz
, la referencia se resuelvePero cuando intentas vincular el programa como:
el orden de los eventos es incorrecto de la misma manera que en el ejemplo 1. En el momento en que el vinculador encuentra
-lz
, no hay referencias a nada en el programa: están todos dentroeg2.o
, lo que aún no se ha visto. Entonces el enlazador decide que no tiene ningún usolibz
. Cuando llegaeg2.o
, lo agrega al programa, y luego tiene una referencia indefinida azlibVersion
, la secuencia de enlace ha finalizado; esa referencia no está resuelta y el enlace falla.Por último, la
pkg-config
variación del ejemplo 2 tiene una explicación ahora obvia. Después de la expansión de la carcasa:se convierte en:
que es solo el ejemplo 2 nuevamente.
Puedo reproducir el problema en el ejemplo 1, pero no en el ejemplo 2
El enlace:
funciona bien para ti!
(O: ese enlace funcionó bien para usted en, digamos, Fedora 23, pero falla en Ubuntu 16.04)
Esto se debe a que la distribución en la que funciona el enlace es una de las que no configura su cadena de herramientas GCC para vincular bibliotecas compartidas según sea necesario .
En el pasado, era normal que los sistemas tipo Unix vinculen bibliotecas estáticas y compartidas por diferentes reglas. Las bibliotecas estáticas en una secuencia de enlace se vincularon según fuera necesario según se explica en el ejemplo 1, pero las bibliotecas compartidas se vincularon incondicionalmente.
Este comportamiento es económico en el momento del enlace porque el enlazador no tiene que pensar si el programa necesita una biblioteca compartida: si es una biblioteca compartida, vincúlela. Y la mayoría de las bibliotecas en la mayoría de los enlaces son bibliotecas compartidas. Pero también hay desventajas:
No es económico en tiempo de ejecución , porque puede hacer que las bibliotecas compartidas se carguen junto con un programa, incluso si no las necesita.
Las diferentes reglas de vinculación para bibliotecas estáticas y compartidas pueden ser confusas para los programadores inexpertos, que pueden no saber si
-lfoo
en su vinculación se resolverá/some/where/libfoo.a
o no/some/where/libfoo.so
, y de todos modos es posible que no entiendan la diferencia entre bibliotecas compartidas y estáticas.Esta compensación ha llevado a la situación cismática hoy. Algunas distribuciones han cambiado sus reglas de enlace de CCG para bibliotecas compartidas, de modo que el principio según sea necesario se aplica a todas las bibliotecas. Algunas distribuciones se han quedado con la vieja forma.
¿Por qué sigo teniendo este problema incluso si compilo y enlazo al mismo tiempo?
Si solo hago:
seguramente gcc tiene que compilar
eg1.c
primero y luego vincular el archivo objeto resultante conlibmy_lib.a
. Entonces, ¿cómo puede no saber que se necesita el archivo de objeto cuando está haciendo el enlace?Porque compilar y vincular con un solo comando no cambia el orden de la secuencia de vinculación.
Cuando ejecuta el comando anterior,
gcc
descubre que desea compilación + vinculación. Así que detrás de las escenas, se genera un comando de compilación, y lo ejecuta, a continuación, genera un comando de vinculación, y lo ejecuta, como si se hubiera ejecute los dos comandos:Por lo que la vinculación falla igual que lo hace si no ejecuta estos dos comandos. La única diferencia que observa en la falla es que gcc ha generado un archivo de objeto temporal en el caso de compilación + enlace, porque no le está diciendo que lo use
eg1.o
. Vemos:en vez de:
Ver también
El orden en que se especifican las bibliotecas vinculadas interdependientes es incorrecto
Poner las bibliotecas interdependientes en el orden incorrecto es solo una forma en la que puede obtener archivos que necesitan definiciones de cosas que vienen más adelante en el enlace que los archivos que proporcionan las definiciones. Poner las bibliotecas antes de los archivos de objetos que se refieren a ellas es otra forma de cometer el mismo error.
fuente
Hacer amistad con las plantillas ...
Dado el fragmento de código de un tipo de plantilla con un operador amigo (o función);
El
operator<<
se declara como una función que no es de plantilla. Para cada tipoT
usado conFoo
, debe haber una no plantillaoperator<<
. Por ejemplo, si hay un tipoFoo<int>
declarado, debe haber una implementación de operador de la siguiente manera;Como no está implementado, el vinculador no puede encontrarlo y genera el error.
Para corregir esto, puede declarar un operador de plantilla antes del
Foo
tipo y luego declarar como amigo, la instancia apropiada. La sintaxis es un poco incómoda, pero tiene el siguiente aspecto;El código anterior limita la amistad del operador a la instanciación correspondiente de
Foo
, es decir, laoperator<< <int>
instanciación se limita al acceso a los miembros privados de la instanciación deFoo<int>
.Las alternativas incluyen;
Permitir que la amistad se extienda a todas las instancias de las plantillas, de la siguiente manera;
O bien, la implementación para el
operator<<
puede hacerse en línea dentro de la definición de clase;Tenga en cuenta que cuando la declaración del operador (o función) solo aparece en la clase, el nombre no está disponible para la búsqueda "normal", solo para la búsqueda dependiente del argumento, de cppreference ;
Hay más información sobre la plantilla de amigos en cppreference y las preguntas frecuentes de C ++ .
Listado de código que muestra las técnicas anteriores .
Como nota al margen del ejemplo de código que falla; g ++ advierte sobre esto de la siguiente manera
fuente
Cuando sus rutas de inclusión son diferentes
Los errores del vinculador pueden ocurrir cuando un archivo de encabezado y su biblioteca compartida asociada (archivo .lib) no se sincronizan. Dejame explicar.
¿Cómo funcionan los enlazadores? El vinculador hace coincidir una declaración de función (declarada en el encabezado) con su definición (en la biblioteca compartida) comparando sus firmas. Puede obtener un error de enlazador si el enlazador no encuentra una definición de función que coincida perfectamente.
¿Es posible obtener un error de vinculador aunque la declaración y la definición parezcan coincidir? ¡Si! Puede que tengan el mismo aspecto en el código fuente, pero realmente depende de lo que vea el compilador. Esencialmente, podrías terminar con una situación como esta:
Observe cómo, aunque ambas declaraciones de funciones se ven idénticas en el código fuente, pero son realmente diferentes según el compilador.
¿Podría preguntar cómo termina uno en una situación como esa? Incluye caminos, por supuesto! Si al compilar la biblioteca compartida, la ruta de inclusión conduce
header1.h
y termina usandoheader2.h
en su propio programa, se quedará rascándose el encabezado preguntándose qué sucedió (juego de palabras).A continuación se explica un ejemplo de cómo puede suceder esto en el mundo real.
Elaboración adicional con un ejemplo
Tengo dos proyectos:
graphics.lib
ymain.exe
. Ambos proyectos dependen decommon_math.h
. Supongamos que la biblioteca exporta la siguiente función:Y luego continúas e incluyes la biblioteca en tu propio proyecto.
¡Auge! Obtiene un error de vinculador y no tiene idea de por qué está fallando. La razón es que la biblioteca común usa diferentes versiones de la misma inclusión
common_math.h
(lo he hecho obvio aquí en el ejemplo al incluir una ruta diferente, pero puede que no siempre sea tan obvio. Quizás la ruta de inclusión sea diferente en la configuración del compilador) .Tenga en cuenta que en este ejemplo, el vinculador le dirá que no puede encontrarlo
draw()
, cuando en realidad sabe que obviamente la biblioteca lo está exportando. Podrías pasar horas rascándote la cabeza preguntándote qué salió mal. La cuestión es que el vinculador ve una firma diferente porque los tipos de parámetros son ligeramente diferentes. En el ejemplo,vec3
es un tipo diferente en ambos proyectos en lo que respecta al compilador. Esto podría suceder porque provienen de dos archivos de inclusión ligeramente diferentes (tal vez los archivos de inclusión provienen de dos versiones diferentes de la biblioteca).Depuración del enlazador
DUMPBIN es tu amigo, si estás usando Visual Studio. Estoy seguro de que otros compiladores tienen otras herramientas similares.
El proceso es así:
[1] Por proyecto me refiero a un conjunto de archivos fuente que están vinculados para producir una biblioteca o un ejecutable.
EDITAR 1: reescribió la primera sección para que sea más fácil de entender. Por favor comente abajo para avisarme si algo más necesita ser reparado. ¡Gracias!
fuente
UNICODE
Definiciones inconsistentesUna compilación de UNICODE de Windows se construye con
TCHAR
etc., que se define comowchar_t
etc. Cuando no se construye conUNICODE
definido como construcción conTCHAR
definido comochar
etc. EstosUNICODE
y_UNICODE
define afectan a todos los "T
" tipos de cadena ;LPTSTR
,LPCTSTR
Y sus alces.Construir una biblioteca con
UNICODE
definido e intentar vincularlo en un proyecto dondeUNICODE
no está definido dará como resultado errores de vinculador ya que habrá una falta de coincidencia en la definición deTCHAR
;char
vswchar_t
.El error generalmente incluye una función un valor con un tipo
char
owchar_t
derivado, estos también podrían incluir,std::basic_string<>
etc. Al navegar a través de la función afectada en el código, a menudo habrá una referenciaTCHAR
aostd::basic_string<TCHAR>
etc. Esta es una señal reveladora de que el código originalmente estaba destinado tanto a un UNICODE como a una compilación de caracteres de varios bytes (o "estrecho") .Para corregir esto, cree todas las bibliotecas y proyectos necesarios con una definición coherente de
UNICODE
(y_UNICODE
).Esto se puede hacer con cualquiera de los dos;
O en la configuración del proyecto;
O en la línea de comando;
La alternativa también es aplicable, si UNICODE no está destinado a ser utilizado, asegúrese de que las definiciones no estén establecidas, y / o la configuración de caracteres múltiples se use en los proyectos y se aplique de manera consistente.
No olvide ser coherente entre las versiones "Release" y "Debug" también.
fuente
Limpiar y reconstruir
Una "limpieza" de la compilación puede eliminar la "madera muerta" que puede quedar por ahí de compilaciones anteriores, compilaciones fallidas, compilaciones incompletas y otros problemas de compilación relacionados con el sistema de compilación.
En general, el IDE o la compilación incluirán alguna forma de función "limpia", pero esto puede no estar configurado correctamente (por ejemplo, en un archivo MAKE manual) o puede fallar (por ejemplo, los binarios intermedios o resultantes son de solo lectura).
Una vez que se haya completado la "limpieza", verifique que la "limpieza" haya tenido éxito y que todo el archivo intermedio generado (por ejemplo, un archivo MAKE automatizado) se haya eliminado con éxito.
Este proceso puede verse como un recurso final, pero a menudo es un buen primer paso ; especialmente si el código relacionado con el error se ha agregado recientemente (ya sea localmente o desde el repositorio de origen).
fuente
Falta "extern" en
const
declaraciones / definiciones variables (solo C ++)Para las personas que vienen de C, puede ser una sorpresa que en C ++ las
const
variables globales tengan un enlace interno (o estático). En C este no fue el caso, ya que todas las variables globales están implícitamenteextern
(es decir, cuandostatic
falta la palabra clave).Ejemplo:
lo correcto sería usar un archivo de encabezado e incluirlo en file2.cpp y file1.cpp
Alternativamente, uno podría declarar la
const
variable en file1.cpp con explícitoextern
fuente
Aunque esta es una pregunta bastante antigua con múltiples respuestas aceptadas, me gustaría compartir cómo resolver un oscuro error de "referencia indefinida a".
Diferentes versiones de bibliotecas.
Estaba usando un alias para hacer referencia a
std::filesystem::path
: el sistema de archivos está en la biblioteca estándar desde C ++ 17 pero mi programa también necesitaba compilar en C ++ 14, así que decidí usar un alias variable:Digamos que tengo tres archivos: main.cpp, file.h, file.cpp:
Tenga en cuenta las diferentes bibliotecas utilizadas en main.cpp y file.h. Como main.cpp # include'd " file.h " después de < filesystem >, la versión del sistema de archivos utilizada era la C ++ 17 . Solía compilar el programa con los siguientes comandos:
$
g++ -g -std=c++17 -c main.cpp
-> compila main.cpp a main.o$
g++ -g -std=c++17 -c file.cpp
-> compila file.cpp y file.h a file.o$
g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> vincula main.o y file.oDe esta manera cualquier función contenida en file.o y se utiliza en main.o que requiere
path_t
dio errores de "referencia" indefinido debido main.o refierestd::filesystem::path
, pero file.o astd::experimental::filesystem::path
.Resolución
Para solucionar esto, solo necesitaba cambiar <experimental :: filesystem> en file.h a <filesystem> .
fuente
Al vincular contra bibliotecas compartidas, asegúrese de que los símbolos utilizados no estén ocultos.
El comportamiento predeterminado de gcc es que todos los símbolos son visibles. Sin embargo, cuando las unidades de traducción se crean con opción
-fvisibility=hidden
, solo las funciones / símbolos marcados con__attribute__ ((visibility ("default")))
son externos en el objeto compartido resultante.Puede verificar si los símbolos que está buscando son externos invocando:
los símbolos ocultos / locales se muestran
nm
con un tipo de símbolo en minúscula, por ejemplo, ent
lugar de `T para la sección de código:También puede usar
nm
con la opción-C
de solicitar los nombres (si se utilizó C ++).Similar a Windows-dlls, uno marcaría las funciones públicas con una definición, por ejemplo
DLL_PUBLIC
definida como:Que corresponde aproximadamente a la versión de Windows / MSVC:
Se puede encontrar más información sobre la visibilidad en el wiki de gcc.
Cuando se compila una unidad de traducción con
-fvisibility=hidden
los símbolos resultantes, todavía tienen un enlace externo (se muestra con el tipo de símbolo en mayúsculanm
) y se puede usar para un enlace externo sin problema si los archivos de objeto se vuelven parte de una biblioteca estática. El enlace se vuelve local solo cuando los archivos de objeto están vinculados a una biblioteca compartida.Para encontrar qué símbolos en un archivo de objeto están ocultos, ejecute:
fuente
nm -CD
onm -gCD
para ver símbolos externos. También vea Visibilidad en el wiki de GCC.Diferentes arquitecturas
Puede ver un mensaje como:
En ese caso, significa que los símbolos disponibles son para una arquitectura diferente a la que está compilando.
En Visual Studio, esto se debe a una "Plataforma" incorrecta, y debe seleccionar la correcta o instalar la versión adecuada de la biblioteca.
En Linux, puede deberse a una carpeta de biblioteca incorrecta (usando en
lib
lugar de,lib64
por ejemplo).En MacOS, existe la opción de enviar ambas arquitecturas en el mismo archivo. Puede ser que el enlace espere que ambas versiones estén allí, pero solo una está. También puede ser un problema con la carpeta
lib
/ incorrectalib64
donde se recoge la biblioteca.fuente