¿Qué es un error de referencia/símbolo externo no definido y cómo lo soluciono?
¿Qué son los errores de referencia/símbolos externos no definidos? ¿Cuáles son las causas comunes y cómo solucionarlas/prevenirlas?
Siéntase libre de editar / agregar el suyo propio.
29 answers
Compilar un programa C++ se lleva a cabo en varios pasos, como se especifica en 2.2 (créditos a Keith Thompson por la referencia):
La precedencia entre las reglas sintácticas de la traducción se especifica mediante las siguientes fases [ver nota al pie].
- Los caracteres de los archivos de origen físicos se asignan, de una manera definida por la implementación, al conjunto de caracteres de origen básico (introducción de caracteres de nueva línea para los indicadores de final de línea) si necesario. [SNIP]
- Cada instancia de un carácter de barra invertida (\) seguida inmediatamente por un carácter de nueva línea se elimina, formar líneas de origen lógicas. [SNIP]
- El archivo fuente se descompone en tokens de preprocesamiento (2.5) y secuencias de caracteres de espacio en blanco (incluidos los comentarios). [SNIP]
- Se ejecutan las directivas de preprocesamiento, se expanden las invocaciones de macro y el operador único _Pragma las expresiones se ejecutan. [SNIP]
- Cada miembro del conjunto de caracteres de origen en un literal de carácter o un literal de cadena, así como cada secuencia de escape y nombre de carácter universal en un literal de carácter o un literal de cadena no raw, se convierte en el miembro correspondiente del conjunto de caracteres de ejecución; [SNIP]
- Los tokens literales de cadena adyacentes se concatenan.
- Los espacios en blanco que separan los tokens ya no son significativos. Cada el token de preprocesamiento se convierte en un token. (2.7). El los tokens resultantes se analizan sintáctica y semánticamente y traducido como una unidad de traducción. [SNIP]
- Las unidades de traducción traducidas y las unidades de instanciación se combinan de la siguiente manera: [SNIP]
- Se resuelven todas las referencias de entidades externas. Los componentes de la biblioteca están vinculados para satisfacer referencias externas a entidades no definidas en el traducción actual. Todo tal traductor la salida se recoge en un imagen del programa que contiene la información necesaria para la ejecución en su entorno de ejecución. (énfasis mío)
[footnote] Las implementaciones deben comportarse como si estas fases separadas ocurrieran, aunque en la práctica diferentes fases podrían estar juntas.
Los errores especificados ocurren durante esta última etapa de compilación, más comúnmente conocida como vinculación. Básicamente significa que compilaste un montón de archivos de implementación en archivos objeto o bibliotecas y ahora desea que funcionen juntos.
Digamos que definiste el símbolo a
en a.cpp
. Ahora, b.cpp
declaró ese símbolo y lo utilizó. Antes de enlazar, simplemente asume que ese símbolo se definió en algún lugar, pero aún no le importa dónde. La fase de enlace es responsable de encontrar el símbolo y enlazarlo correctamente a b.cpp
(bueno, en realidad al objeto o biblioteca que lo usa).
Si eres usando Microsoft Visual Studio, verás que los proyectos generan archivos .lib
. Estos contienen una tabla de símbolos exportados, y una tabla de símbolos importados. Los símbolos importados se resuelven contra las bibliotecas con las que se enlaza, y los símbolos exportados se proporcionan para las bibliotecas que usan .lib
(si las hay).
Existen mecanismos similares para otros compiladores/ plataformas.
Los mensajes de error comunes son error LNK2001
, error LNK1120
, error LNK2019
para Microsoft Visual Studio y undefined reference to
symbolNamefor GCC.
El código:
struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
struct A
{
virtual ~A() = 0;
};
struct B: A
{
virtual ~B(){}
};
extern int x;
void foo();
int main()
{
x = 0;
foo();
Y y;
B b;
}
Generará los siguientes errores con GCC :
/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status
Y errores similares con Microsoft Visual Studio:
1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals
Las causas comunes incluyen:
- Fallo al enlazar con bibliotecas/archivos objeto apropiados o compilar archivos de implementación
- Variable o función declarada e indefinida.
- problemas Comunes con miembros de tipo clase
- Implementaciones de plantillas no visibles.
- Los símbolos fueron definidos en un programa C y usados en código C++.
- Importación/exportación incorrecta de métodos / clases a través de módulos / dll. (Específicos de MSV)
- Dependencia de la biblioteca circular
- referencia indefinida a 'WinMain@16'
- Orden interdependiente de bibliotecas
- Múltiples archivos de origen de el mismo nombre
-
Escribir mal o no incluir el .extensión lib cuando se utiliza el
#pragma
(Microsoft Visual Studio) - Problemas con los amigos de la plantilla
-
Inconsistentes
UNICODE
definiciones
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-18 03:51:35
Miembros de la clase:
Un destructor puro virtual
necesita una implementación.
Declarar un destructor puro aún requiere que lo definas (a diferencia de una función regular):
struct X
{
virtual ~X() = 0;
};
struct Y : X
{
~Y() {}
};
int main()
{
Y y;
}
//X::~X(){} //uncomment this line for successful definition
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 métodos no-virtual
sin definición, con el añadido razonando que
la declaración pure genera una vtable ficticia y puede obtener el error del enlazador sin usar la función:
struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
int main()
{
Y y; //linker error although there was no call to X::foo
}
Para que esto funcione, declarad X::foo()
como puro:{[25]]}
struct X
{
virtual void foo() = 0;
};
No - virtual
miembros de la clase
Algunos miembros deben definirse incluso si no se utilizan explícitamente:
struct A
{
~A();
};
Lo siguiente produciría el error:
A a; //destructor undefined
La implementación puede ser inline, en la propia definición de clase:
struct A
{
~A() {}
};
O exterior:
A::~A() {}
Si la implementación está fuera de la definición de la clase, pero en un encabezado, los métodos deben marcarse como inline
para evitar una definición múltiple.
Todos los métodos miembros utilizados deben definirse si se utilizan.
Un error común es olvidar calificar el nombre:
struct A
{
void foo();
};
void foo() {}
int main()
{
A a;
a.foo();
}
La definición debe ser
void A::foo() {}
static
los miembros de datos deben definirse fuera de la clase en una unidad de traducción única :
struct X
{
static int x;
};
int main()
{
int x = X::x;
}
//int X::x; //uncomment this line to define X::x
An inicializador se puede proporcionar para un static
const
miembro de datos de tipo integral o enumeración dentro de la definición de clase; sin embargo, el uso de odr de este miembro aún requerirá una definición de ámbito de espacio de nombres como se describió anteriormente. C++11 permite la inicialización dentro de la clase para todos los miembros de datos static const
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-09 01:06:32
Fallo al enlazar con bibliotecas/archivos objeto apropiados o compilar archivos de implementación
Comúnmente, cada unidad de traducción generará un archivo objeto que contiene las definiciones de los símbolos definidos en esa unidad de traducción. Para usar esos símbolos, tienes que enlazar contra esos archivos objeto.
En gcc usted especificaría todos los archivos objeto que se van a vincular en la línea de comandos, o compilaría los archivos de implementación junto.
g++ -o test objectFile1.o objectFile2.o -lLibraryName
El libraryName
aquí es solo el nombre desnudo de la biblioteca, sin adiciones específicas de la plataforma. Así, por ejemplo, en Linux los archivos de la biblioteca se llaman normalmente libfoo.so
pero solo escribirías -lfoo
. En Windows ese mismo archivo podría llamarse foo.lib
, pero usarías el mismo argumento. Es posible que tenga que agregar el directorio donde se pueden encontrar esos archivos usando -L‹directory›
. Asegúrese de no escribir un espacio después de -l
o -L
.
Para XCode : Agregue las rutas de búsqueda de Encabezado de usuario - > agregar la ruta de búsqueda de la biblioteca - > arrastre y suelte la referencia de la biblioteca real en la carpeta del proyecto.
Bajo MSVS, los archivos agregados a un proyecto automáticamente tienen sus archivos objeto vinculados entre sí y se generaría un archivo lib
(en uso común). Para usar los símbolos en un proyecto separado,
es necesario incluir los archivos lib
en la configuración del proyecto. Esto se hace en la sección Enlazador de las propiedades del proyecto, en Input -> Additional Dependencies
. (la ruta al archivo lib
debe ser
añadido en Linker -> General -> Additional Library Directories
) Cuando se utiliza una biblioteca de terceros que se proporciona con un archivo lib
, si no lo hace, generalmente se produce el error.
También puede suceder que se olvide de agregar el archivo a la compilación, en cuyo caso el archivo objeto no se generará. En gcc añadirías los archivos a la línea de comandos. 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 Programación de Windows, el signo revelador de que no vinculó una biblioteca necesaria es que el nombre del símbolo sin resolver 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 pone la información en un cuadro en la parte inferior de cada función en una sección llamada "Biblioteca".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-23 09:32:00
Declaró pero no definió una variable o función.
Una declaración de variable típica es
extern int x;
Como esto es solo una declaración, se necesita una definición única. Una definición correspondiente sería:
int x;
Por ejemplo, lo siguiente generaría un error:
extern int x;
int main()
{
x = 0;
}
//int x; // uncomment this line for successful definition
Observaciones similares se aplican a las funciones. Declarar una función sin definirla conduce al error:
void foo(); // declaration only
int main()
{
foo();
}
//void foo() {} //uncomment this line for successful definition
Tenga cuidado de que la función que implementa coincida exactamente con la uno que declaraste. Por ejemplo, es posible que tenga calificadores de CV no coincidentes:
void foo(int& x);
int main()
{
int x;
foo(x);
}
void foo(const int& x) {} //different function, doesn't provide a definition
//for void foo(int& x)
Otros ejemplos de desajustes incluyen
- Función/variable declarada en un espacio de nombres, definida en otro.
- Función/variable declarada como miembro de clase, definida como global (o viceversa).
- El tipo de retorno de la función, el número de parámetro y los tipos, y la convención de llamada no están todos exactamente de acuerdo.
El mensaje de error del compilador a menudo le dará el declaración de la variable o función que fue declarada pero nunca definida. Compáralo de cerca con la definición que proporcionaste. Asegúrese de que cada detalle coincida.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-23 15:54:29
El orden en el que se especifican las bibliotecas vinculadas interdependientes es incorrecto.
El orden en el que las bibliotecas están vinculadas importa si las bibliotecas dependen unas de otras. En general, si library A
depende de library B
, entonces libA
DEBE aparecer antes de libB
en las banderas del enlazador.
Por ejemplo:
// B.h
#ifndef B_H
#define B_H
struct B {
B(int);
int x;
};
#endif
// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}
// A.h
#include "B.h"
struct A {
A(int x);
B b;
};
// A.cpp
#include "A.h"
A::A(int x) : b(x) {}
// main.cpp
#include "A.h"
int main() {
A a(5);
return 0;
};
Crear las bibliotecas:
$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o
ar: creating libB.a
a - B.o
Compilar:
$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out
Así que para repetir de nuevo, el orden HACE importa!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-10 11:46:08
Qué es una "referencia indefinida/símbolo externo no resuelto"
Trataré de explicar qué es una "referencia indefinida/símbolo externo no resuelto".
Nota: uso g++ y Linux y todos los ejemplos son para ello
Por ejemplo tenemos algún código
// src1.cpp
void print();
static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;
int main()
{
print();
return 0;
}
Y
// src2.cpp
extern "C" int printf (const char*, ...);
extern int global_var_name;
//extern int local_var_name;
void print ()
{
// printf("%d%d\n", global_var_name, local_var_name);
printf("%d\n", global_var_name);
}
Hacer archivos objeto
$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o
Después de la fase de ensamblador tenemos un archivo objeto, que contiene cualquier símbolo a exportar. Mira el símbolos
$ readelf --symbols src1.o
Num: Value Size Type Bind Vis Ndx Name
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 _ZL14local_var_name # [1]
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_var_name # [2]
He rechazado algunas líneas de salida, porque no importan
Por lo tanto, vemos seguir símbolos para exportar.
[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable
Src2.cpp no exporta nada y no hemos visto sus símbolos
Enlazar nuestros archivos objeto
$ g++ src1.o src2.o -o prog
Y ejecutarlo
$ ./prog
123
El enlazador ve los símbolos exportados y los vincula. Ahora tratamos de descomentar líneas en src2.cpp como aquí
// src2.cpp
extern "C" int printf (const char*, ...);
extern int global_var_name;
extern int local_var_name;
void print ()
{
printf("%d%d\n", global_var_name, local_var_name);
}
Y reconstruir un archivo objeto
$ g++ -c src2.cpp -o src2.o
OK (sin errores), porque solo construir archivo de objeto, la vinculación no se hace todavía. Intenta vincular
$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status
Ha ocurrido porque nuestro local_var_name es estático, es decir, no es visible para otros módulos. Ahora más profundamente. Obtener la salida de la fase de traducción
$ g++ -S src1.cpp -o src1.s
// src1.s
look src1.s
.file "src1.cpp"
.local _ZL14local_var_name
.comm _ZL14local_var_name,4,4
.globl global_var_name
.data
.align 4
.type global_var_name, @object
.size global_var_name, 4
global_var_name:
.long 123
.text
.globl main
.type main, @function
main:
; assembler code, not interesting for us
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
Por lo tanto, hemos visto que no hay ninguna etiqueta para local_var_name, es por eso que linker no la ha encontrado. Pero somos hackers :) y podemos arreglarlo. Abra src1.s en su editor de texto y cambiar
.local _ZL14local_var_name
.comm _ZL14local_var_name,4,4
A
.globl local_var_name
.data
.align 4
.type local_var_name, @object
.size local_var_name, 4
local_var_name:
.long 456789
Es decir, usted debe tener como abajo
.file "src1.cpp"
.globl local_var_name
.data
.align 4
.type local_var_name, @object
.size local_var_name, 4
local_var_name:
.long 456789
.globl global_var_name
.align 4
.type global_var_name, @object
.size global_var_name, 4
global_var_name:
.long 123
.text
.globl main
.type main, @function
main:
; ...
Hemos cambiado la visibilidad de local_var_name y establecido su valor en 456789. Intenta construir un archivo objeto a partir de él
$ g++ -c src1.s -o src2.o
Ok, ver readelf output (símbolos)
$ readelf --symbols src1.o
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
Ahora local_var_name tiene Bind GLOBAL (era LOCAL)
Enlace
$ g++ src1.o src2.o -o prog
Y ejecutarlo
$ ./prog
123456789
Ok, lo hackeamos:)
Entonces, como resultado, se produce un "error de referencia no definida/símbolo externo no resuelto" cuando el enlazador no puede encontrar símbolos globales en archivos objeto.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-09 13:30:37
Los símbolos fueron definidos en un programa C y usados en código C++.
La función (o variable) void foo()
se definió en un programa C y se intenta usarla en un programa C++:
void foo();
int main()
{
foo();
}
El enlazador de C++ espera que los nombres sean mutilados, por lo que debe declarar la función como:
extern "C" void foo();
int main()
{
foo();
}
De forma 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:
extern "C" void foo();
E intenta usarlo en un programa C++ con C++ vinculación.
Si se incluye una biblioteca completa en un archivo de encabezado (y se compiló como código C); la inclusión tendrá que ser la siguiente;
extern "C" {
#include "cheader.h"
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-17 07:25:35
Si todo lo demás falla, recompile.
Recientemente pude deshacerme de un error externo no resuelto en Visual Studio 2012 simplemente recompilando el archivo ofensivo. Cuando reconstruí, el error desapareció.
Esto suele ocurrir 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. Ninguno existe para empezar. Cuando intenta compilar A, el paso de enlace fallará porque no puede encontrar a B. Lib. A. se generará lib, pero no dll. Luego compila B, que tendrá éxito y generará B. lib. Volver a compilar A ahora funcionará porque ahora se encuentra B. lib.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-03 20:56:14
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 funcionalidad dual se obtiene generalmente mediante el uso de una macro:
#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif
La macro THIS_MODULE
solo se definiría en el módulo que exporta la función. Así, la declaración:
DLLIMPEXP void foo();
Se expande a
__declspec(dllexport) void foo();
Y le dice al compilador que función, ya que el módulo actual contiene su definición. Al incluir la declaración en un módulo diferente, se ampliaría a
__declspec(dllimport) void foo();
Y le dice al compilador que la definición está en una de las bibliotecas con las que ha enlazado (véase también 1)).
Puede importar / exportar clases similares:
class DLLIMPEXP X
{
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-18 06:26:07
Las implementaciones de plantillas no son visibles.
Las plantillas no especializadas deben tener sus definiciones visibles para todas las unidades de traducción que las utilicen. 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 archivo impl
que incluya al final del encabezado que
declara la plantilla. Una situación común es:
template<class T>
struct X
{
void foo();
};
int main()
{
X<int> x;
x.foo();
}
//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}
Para arreglar esto, debe mover la definición de X::foo
a la archivo de cabecera o algún lugar visible para la unidad de traducción que lo utiliza.
Las plantillas especializadas se pueden implementar en un archivo de implementación y la implementación no tiene que ser visible, pero la especialización debe declararse previamente.
Para mayor explicación y otra posible solución (instanciación explícita) ver esta pregunta y respuesta.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:46
Este es uno de los mensajes de error más confusos que todos los programadores de VC++ han visto una y otra vez. Primero hagamos las cosas claras.
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++. Está 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 objeto (.obj) para la actual dependencia de traducción. (Tenga en cuenta que cada archivo de cabecera que este archivo fuente incluido será preprocesado y se considerará como parte de esta unidad de traducción)Todo dentro de una unidad de traducción se considera como interno, todo lo demás se considera como externo. En C++, puede hacer referencia a un símbolo externo utilizando palabras clave como extern
, __declspec (dllimport)
y así sucesivamente.
C. ¿Qué es "resolver"? Resolve es un término de tiempo de enlace. En linking-time, linker intenta encontrar la definición externa para cada símbolo en los archivos objeto que no pueden encontrar su definición internamente. El alcance de este proceso de búsqueda incluye:
- Todos los archivos objeto que se generan en tiempo de compilación
- Todas las bibliotecas (.lib) que son explícita o implícitamente especificado como dependencias adicionales de este edificio aplicación.
Este proceso de búsqueda se llama resolve.
D. Finalmente, ¿por qué Un Símbolo Externo Sin Resolver? Si el enlazador no puede encontrar la definición externa de un símbolo que no tiene definición interna, informa de 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 se puede ordenar como:
- La definición existe
Por ejemplo, si tenemos una función llamada foo definida en a.cpp:
int foo()
{
return 0;
}
En b.cpp queremos llamar a la función foo, por lo que añadimos
void foo();
Para declarar function foo (), y llamarla en otro cuerpo de función, diga bar()
:
void bar()
{
foo();
}
Ahora, cuando construya este código, obtendrá un error LNK2019 quejándose de que foo es un símbolo sin resolver. En este caso, sabemos que foo() tiene su definición en a.cpp, pero diferente de la que estamos llamando (diferente valor de retorno). Este es el caso de que la definición existe.
- La definición no existe
Si queremos llamar a algunas funciones de una biblioteca, pero la biblioteca de importación no se agrega a la lista de dependencias adicionales (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 ámbito de búsqueda actual.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-08 09:05:14
Referencia indefinida a WinMain@16
o similar 'inusual' main()
referencia del punto de entrada (especialmente para visual-studio ).
Es posible que no haya elegido el tipo de proyecto correcto con su IDE real. El IDE puede querer enlazar, por ejemplo, proyectos de aplicaciones de Windows a dicha función de punto de entrada (como se especifica en la referencia faltante anterior), en lugar de la firma comúnmente utilizada int main(int argc, char** argv);
.
Si su IDE es compatible con Proyectos de consola simples es posible que desee elija 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 desde un problema del mundo real.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-12 17:39:25
Además, si está utilizando bibliotecas de terceros, asegúrese de tener los binarios correctos de 32/64 bits
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-18 17:06:24
Microsoft ofrece un #pragma
para hacer referencia a la biblioteca correcta en el momento del enlace;
#pragma comment(lib, "libname.lib")
Además De la ruta de la biblioteca, incluyendo el directorio de la biblioteca, este debe ser el nombre completo de la biblioteca.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-10 06:37:35
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 de paquete solo tenía bibliotecas para Visual Studio 2010 y 2012.
La solución correcta es esperar que el desarrollador publique un paquete actualizado y luego actualice, pero funcionó para mí al hackear una configuración adicional para VS2013, apuntando a los archivos de la biblioteca VS2012.
Edité el paquete (en la carpeta packages
dentro del directorio de la solución) encontrando packagename\build\native\packagename.targets
y dentro de ese archivo, copiando todas las secciones v110
. Cambié el v110
a v120
en los campos de condición solo teniendo mucho cuidado de dejar las rutas del nombre de archivo como v110
. Esto simplemente permitió que Visual Studio 2013 se vinculara a las bibliotecas de 2012, y en este caso, funcionó.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-28 03:52:36
Supongamos que tienes 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 toda la solución ( supongamos que solo tenemos un proyecto en la solución)
¿Cuál es el significado de compilación ?
- Visual Studio buscar en el archivo .vcxproj y empezar a compilar cada archivo que tiene la extensión .cpp. Orden de compilación es undefined.So no debe asumir que el archivo principal.cpp se compila primero
- Si .archivos cpp depende de adicional .archivos h para encontrar símbolos eso puede o no estar definido en el archivo .cpp
- Si existe uno .archivo cpp en el que el compilador no pudo encontrar un símbolo, un error de tiempo del compilador levanta el mensaje El símbolo x no pudo ser encontrado
- Para cada archivo con extensión .cpp se genera un archivo objeto .o y también Visual Studio escribe la salida en un archivo llamado ProjectName.Cpp.Limpiar.txt que contiene todos los archivos objeto que deben ser procesados por el enlazador.
El Segundo paso de la compilación lo realiza el Enlazador.El enlazador debe fusionar todo el archivo objeto y construir finalmente la salida ( que puede ser un ejecutable o una biblioteca)
Pasos Para Vincular un proyecto
- Analizar todos los archivos objeto y encontrar la definición que solo se declaró en encabezados (por ejemplo: El código de un método de una clase como se menciona en respuestas anteriores, o evento la inicialización de una variable estática que es miembro dentro de una clase)
- Si no se pudo encontrar un símbolo en los archivos objeto, también se busca en Bibliotecas Adicionales.Para agregar una nueva biblioteca a un proyecto Propiedades de configuración -> Directorios VC++ -> Biblioteca Directorios y aquí especificó una carpeta adicional para buscar bibliotecas y propiedades de configuración -> Enlazador -> Input para especificar el nombre de la biblioteca.
-Si el Enlazador no pudo encontrar el símbolo que escribes en uno .cpp plantea un error de tiempo enlazador que puede sonar como
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
Observación
- Una vez que el Enlazador encuentra un símbolo, no lo busca en otras bibliotecas
- El orden de enlazar bibliotecas sí importa.
- Si el enlazador encuentra un símbolo externo en una biblioteca estática, lo incluye en la salida del proyecto.Sin embargo, si la biblioteca es compartida( dinámica ) no incluye el código ( símbolos ) en la salida, pero En tiempo de ejecución pueden ocurrir bloqueos
Cómo resolver este tipo de error
Error de Tiempo del Compilador:
- Asegúrese de escribir correctamente la sintaxis de su proyecto c++.
Enlazador Error de tiempo
- Define todos los símbolos que declaras en tus archivos de cabecera
- Use
#pragma once
para permitir que el compilador no incluya un encabezado si ya estaba incluido en el actual .cpp que se compilan - Asegúrese de que su biblioteca externa no contenga símbolos que puedan entrar en conflicto con otros símbolos que haya definido en sus archivos de encabezado
- Cuando utilice la plantilla para asegurarse de incluir la definición de cada función de plantilla en el encabezado archivo para permitir que el compilador genere el código apropiado para cualquier instancia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-08 10:15:57
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 añadirlo para superar el error.
Pasos a seguir si crees que podría ser un error en el compilador / IDE:
- Limpie el proyecto (algunos IDE tienen una opción para hacer esto, también puede hacerlo manualmente eliminando los archivos objeto)
- Intente iniciar un nuevo proyecto, copiar todo el código fuente de el original.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:30
Vinculado .el archivo lib 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 fue producido como el DLL para el MyProject fue construido. Además, no contuve código fuente para todos los métodos en el MyProject, sino solo acceso a los puntos de entrada de la DLL.
Para resolver el problema, construí el MyProject como una LIB, y vinculé TestProject a esto .lib archivo (copio y pego el generado .archivo lib en la carpeta TestProject). Entonces puedo construir de nuevo MyProject como DLL. Está compilando ya que la lib a la que está enlazado TestProject contiene código para todos los métodos en clases en MyProject.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-04 15:02:33
Utilice el enlazador para ayudar a diagnosticar el error
La mayoría de los enlazadores modernos incluyen una opción detallada que imprime en diversos grados;
- Invocación de enlace (línea de comandos),
- Datos sobre qué bibliotecas se incluyen en la etapa de enlace,
- La ubicación de las bibliotecas,
- Rutas de búsqueda utilizadas.
Para gcc y clang; normalmente agregaría -v -Wl,--verbose
o -v -Wl,-v
a la línea de comandos. Más detalles se pueden encontrar aquí;
- Linux ld man page .
- LLVM página enlazadora .
- "Una introducción al CCG" capítulo 9.
Para MSVC, /VERBOSE
(en particular /VERBOSE:LIB
) se añade a la línea de comandos link.
- La página de MSDN en el
/VERBOSE
opción de enlazador .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-24 10:41:08
Dado que la gente parece estar dirigida a esta pregunta cuando se trata de errores de enlazador, voy a agregar esto aquí.
Una posible razón para los errores del enlazador con GCC 5.2.0 es que ahora se elige una nueva biblioteca ABI de libstdc++ por defecto.
Si obtiene errores de enlazador sobre referencias indefinidas a símbolos que involucran tipos en el espacio de nombres std::__cxx11 o la etiqueta [abi: cxx11], probablemente indique que está tratando de vincular archivos objeto que fueron compilados con diferentes valores para la macro _GLIBCXX_USE_CXX11_ABI. Esto sucede comúnmente cuando se enlaza a una biblioteca de terceros que se compiló con una versión anterior de GCC. Si la biblioteca de terceros no se puede reconstruir con la nueva ABI, entonces tendrá que recompilar su código con la ABI antigua.
Así que si de repente tienes errores de enlazador al cambiar a un GCC después de 5.1.0 esto sería una cosa para comprobar.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-10 11:03:35
Un envoltorio alrededor de GNU ld que no soporta scripts enlazadores
Algunos archivos. so son en realidad scripts enlazadores ld de GNU , por ejemplo libtbb.so el archivo es un archivo de texto ASCII con este contenido:
INPUT (libtbb.so.2)
Algunas compilaciones más complejas pueden no soportar esto. Por ejemplo, si incluye-v en las opciones del compilador, puede ver que el mainwin gcc wrapper mwdip descarta los archivos de comandos del script del enlazador en la lista de salida detallada de bibliotecas para vincular. Un un trabajo simple es reemplazar el archivo de comandos de entrada del script enlazador con una copia del archivo en su lugar (o un enlace simbólico), por ejemplo,
cp libtbb.so.2 libtbb.so
O podría 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-30 20:47:37
Hacer amistad con plantillas...
Dado el fragmento de código de un tipo de plantilla con un operador amigo (o función);
template <typename T>
class Foo {
friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};
El operator<<
se declara como una función que no es plantilla. Para cada tipo T
usado con Foo
, debe haber un operator<<
no templado. Por ejemplo, si hay un tipo Foo<int>
declarado, entonces debe haber una implementación de operador de la siguiente manera;
std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
Dado que no está implementado, el enlazador no lo encuentra y resulta en error.
Para corregir esto, puede declarar un operador de plantilla antes del tipo Foo
y luego declarar como amigo, la instanciación apropiada. La sintaxis es un poco incómoda, pero se ve de la siguiente manera;
// forward declare the Foo
template <typename>
class Foo;
// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&);
template <typename T>
class Foo {
friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
// note the required <> ^^^^
// ...
};
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&)
{
// ... implement the operator
}
El código anterior limita la amistad del operador a la instanciación correspondiente de Foo
, es decir, la instanciación operator<< <int>
está limitada al acceso a los miembros privados de la instanciación de Foo<int>
.
Alternativas include;
-
Permitiendo que la amistad se extienda a todas las instancias de las plantillas, de la siguiente manera;
template <typename T> class Foo { template <typename T1> friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a); // ... };
-
O bien, la implementación para el
operator<<
se puede hacer en línea dentro de la definición de la clase;template <typename T> class Foo { friend std::ostream& operator<<(std::ostream& os, const Foo& a) { /*...*/ } // ... };
Nota , 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, desde cppreference ;
Un nombre declarado por primera vez en una declaración de amigo dentro de la clase o la plantilla de clase X se convierte en un miembro del espacio de nombres más interno de X, pero no es accesible para la búsqueda (excepto la búsqueda dependiente del argumento que considera X) a menos que se proporcione una declaración coincidente en el ámbito del espacio de nombres...
Hay más información sobre amigos de plantillas en cppreference y el C++ FAQ.
Listado de código que muestra las técnicas above .
Como nota al margen del ejemplo de código fallido; g++ advierte sobre esto de la siguiente manera
warning: friend declaration 'std::ostream& operator<<(...)' declares a non-template function [-Wnon-template-friend]
note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-10 21:29:24
Inconsistente UNICODE
definiciones
Una compilación de Windows UNICODE se construye con TCHAR
etc. se define como wchar_t
etc. Cuando no se construye con UNICODE
definido como construir con TCHAR
definido como char
etc. Estos UNICODE
y _UNICODE
define afectan a todos los "T
" tipos de cadenas; LPTSTR
, LPCTSTR
y sus alces.
Construir una biblioteca con UNICODE
definida e intentar vincularla en un proyecto donde UNICODE
no está definida dará lugar a errores de enlazador ya que habrá un desajuste en la definición de TCHAR
; char
vs wchar_t
.
El error generalmente incluye una función un valor con un tipo derivado char
o wchar_t
, estos podrían incluir std::basic_string<>
etc. También. Al navegar a través de la función afectada en el código, a menudo habrá una referencia a TCHAR
o std::basic_string<TCHAR>
, etc. Este es un signo revelador de que el código fue diseñado originalmente para una construcción UNICODE y Multi-Byte Character (o "narrow").
Para corregir esto, construya todas las bibliotecas necesarias y proyectos con una definición coherente de UNICODE
(y _UNICODE
).
-
Esto se puede hacer con cualquiera;
#define UNICODE #define _UNICODE
-
O en la configuración del proyecto;
Propiedades del proyecto > General > Valores predeterminados del Proyecto > Conjunto de caracteres
-
O en la línea de comandos;
/DUNICODE /D_UNICODE
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 el multi-carácter la configuración se utiliza en los proyectos y se aplica consistentemente.
No se olvide de ser consistente entre las compilaciones "Release" y "Debug" también.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-12 06:06:08
Limpiar y reconstruir
Una "limpieza" de la construcción puede eliminar la "madera muerta" que puede quedar por ahí de construcciones anteriores, construcciones fallidas, construcciones incompletas y otros problemas de construcción relacionados con el sistema de construcció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 makefile manual) o puede fallar (por ejemplo, los binarios intermedios o resultantes son de solo lectura).
Una vez que la" limpieza " se ha completado, verifique que el "limpiar" ha tenido éxito y todo el archivo intermedio generado (por ejemplo, un makefile automatizado) se ha 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 fuente).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-24 12:41:09
Su enlace consume bibliotecas antes que los archivos objeto que se refieren a ellas
- Está tratando de compilar y vincular su programa con la cadena de herramientas GCC.
- Su enlace especifica todas las bibliotecas necesarias y rutas de búsqueda de bibliotecas
- Si
libfoo
dependelibbar
, entonces su vinculación correctamente ponelibfoo
antes delibbar
. - Su enlace falla con
undefined reference to
algo errores. - Pero todo lo indefinido algo s se declaran en los archivos de cabecera que tiene
#include
d y de hecho se definen en las bibliotecas que está enlazando.
Los ejemplos están en C. También podrían ser C++
Un ejemplo mínimo que involucra una biblioteca estática que usted mismo construyó
My_lib.c
#include "my_lib.h"
#include <stdio.h>
void hw(void)
{
puts("Hello World");
}
My_lib.h
#ifndef MY_LIB_H
#define MT_LIB_H
extern void hw(void);
#endif
Eg1.c
#include <my_lib.h>
int main()
{
hw();
return 0;
}
Construyes tu biblioteca estática:
$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o
Usted compila su programa:
$ gcc -I. -c -o eg1.o eg1.c
Intenta vincularlo con libmy_lib.a
y falla:
$ gcc -o eg1 -L. -lmy_lib eg1.o
eg1.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status
El mismo resultado si compila y enlaza en un solo paso, como:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
/tmp/ccQk1tvs.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status
Un ejemplo mínimo que involucra una biblioteca de sistema compartida, la biblioteca de compresión libz
Eg2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%s\n",zlibVersion());
return 0;
}
Compila tu programa:
$ gcc -c -o eg2.o eg2.c
Intenta vincular tu programa con libz
y falla:
$ gcc -o eg2 -lz eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status
Lo mismo si compilas y enlazas de una sola vez:
$ gcc -o eg2 -I. -lz eg2.c
/tmp/ccxCiGn7.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status
Y a variación del ejemplo 2 que involucra pkg-config
:
$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
¿Qué estás haciendo mal?
En la secuencia de archivos objeto y bibliotecas que desea vincular para hacer su programa, está colocando las bibliotecas antes de los archivos objeto que se refieren a ellos. Debe colocar las bibliotecas después de los archivos objeto que se refieren por ellos.
Enlace ejemplo 1 correctamente:
$ gcc -o eg1 eg1.o -L. -lmy_lib
Éxito:
$ ./eg1
Hello World
Ejemplo de enlace 2 correctamente:
$ gcc -o eg2 eg2.o -lz
Éxito:
$ ./eg2
1.2.8
Enlaza correctamente el ejemplo 2 pkg-config
variación:
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
La explicación
La lectura es opcional de aquí en adelante.
De forma predeterminada, un comando de enlace generado por GCC, en su distribución, consume los archivos en el enlace de izquierda a derecha en secuencia de línea de comandos. Cuando encuentra que un archivo se refiere a algo y no contiene una definición para ello, para buscar un definición en archivos más a la derecha. Si finalmente encuentra una definición, el la referencia está resuelta. 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 objeto. Cuando el enlazador
encuentra -lmy_lib
en la secuencia de enlace y se da cuenta de que esto se refiere
a la biblioteca estática ./libmy_lib.a
, quiere saber si su programa
necesita cualquiera de los archivos objeto en libmy_lib.a
.
Solo hay un archivo objeto en libmy_lib.a
, a saber, my_lib.o
, y solo hay una cosa definida
en my_lib.o
, es decir, la función hw
.
El enlazador decidirá que su programa necesita my_lib.o
si y solo si ya lo sabe
su programa se refiere a hw
, en uno o más de los archivos objeto que ya tiene
añadido al programa, y que ninguno de los archivos objeto ya ha añadido
contiene una definición para hw
.
Si eso es cierto, entonces el enlazador extraerá una copia de my_lib.o
de la biblioteca y
agrégalo a tu programa. Entonces, su programa contiene una definición para hw
, así
sus referencias a hw
son resueltas.
Cuando intenta vincular el programa como:
$ gcc -o eg1 -L. -lmy_lib eg1.o
El enlazador no ha añadido eg1.o
al programa cuando ve
-lmy_lib
. Porque en ese momento, no ha visto eg1.o
.
Su programa todavía no hace ninguna referencias a hw
: it
todavía no hace ninguna referencia en absoluto, porque todas las referencias que hace
están en eg1.o
.
Así que el enlazador no agrega my_lib.o
al programa y no tiene más
utilizar para libmy_lib.a
.
Luego, encuentra eg1.o
, y lo agrega como programa. Un archivo objeto en el
la secuencia de enlace siempre se agrega al programa. Ahora, el programa hace
una referencia a hw
, y no contiene una definición de hw
; pero
no queda nada en el enlace secuencia que podría proporcionar la falta
definición. La referencia a hw
termina sin resolver, y el enlace falla.
Segundo, ejemplo 2, con biblioteca compartida libz
Una biblioteca compartida no es un archivo de archivos objeto ni nada parecido. Es
mucho más como un programa que no tiene una función main
y
en su lugar expone varios otros símbolos que define, de modo que otros
los programas pueden usarlos en ejecución.
Muchas distribuciones de Linux hoy configuran su cadena de herramientas GCC para que sus controladores de idioma(gcc
,g++
,gfortran
etc)
indique al enlazador del sistema (ld
) que vincule bibliotecas compartidas en según sea necesario.
Tienes una de esas distros.
Esto significa que cuando el enlazador encuentra -lz
en la secuencia de enlace, y se da cuenta de que esto se refiere
a la biblioteca compartida (por ejemplo) /usr/lib/x86_64-linux-gnu/libz.so
, quiere saber si alguna referencia que ha añadido a su programa que aún no están definidos tienen definiciones que son exportadas por libz
Si eso es cierto, entonces el enlazador no copiará cualquier fragmento de libz
y
añádalos a su programa; en su lugar, solo medirá el código de su programa
así que: -
En tiempo de ejecución, el cargador de programa del sistema cargará una copia de
libz
en el el mismo proceso que su programa cada vez que se carga una copia de su programa, para ejecutarlo.En tiempo de ejecución, siempre que su programa se refiere a algo que se define en
libz
, esa referencia utiliza la definición exportada por la copia delibz
en el mismo proceso.
Su programa quiere hacer referencia a una sola cosa que tiene una definición exportada por libz
,
es decir, la función zlibVersion
, a la que se hace referencia una sola vez, en eg2.c
.
Si el enlazador agrega esa referencia a su programa, y luego encuentra la definición
exportados por libz
, la referencia es resuelto
Pero cuando intenta vincular el programa como:
gcc -o eg2 -lz eg2.o
El orden de los eventos es incorrecto de la misma manera que con el ejemplo 1.
En el punto en que el enlazador encuentra -lz
, hay no referencias a nada
en el programa: están todos en eg2.o
, que aún no se ha visto. Así que el
el enlazador decide que no tiene uso para libz
. Cuando llega a eg2.o
, lo agrega al programa,
y luego tiene referencia indefinida a zlibVersion
, la secuencia de enlace está terminada;
esa referencia no está resuelta, y la conexión falla.
Por último, la variación pkg-config
del ejemplo 2 tiene una explicación ahora obvia.
Después de la expansión del shell:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
Se convierte en:
gcc -o eg2 -lz eg2.o
Que es solo el ejemplo 2 de nuevo.
Puedo reproducir el problema en el ejemplo 1, pero no en el ejemplo 2
El vínculo:
gcc -o eg2 -lz eg2.o
Funciona muy bien para usted!
(O: Ese enlace funcionó bien para usted en, digamos, Fedora 23, pero falla en Ubuntu 16.04)
Eso es porque 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 su día, era normal que los sistemas tipo unix enlazaran estática y compartida bibliotecas por diferentes reglas. Se vincularon bibliotecas estáticas en una secuencia de enlace en la base según sea necesario explicada en el ejemplo 1, pero las bibliotecas compartidas estaban vinculadas incondicionalmente.
Este comportamiento es económico en linktime porque el enlazador no tiene que reflexionar si el programa necesita una biblioteca compartida: si se trata de una biblioteca compartida, enlázalo. Y la mayoría de las bibliotecas en la mayoría de los enlaces son bibliotecas compartidas. Pero también hay desventajas:-
Es antieconómico en tiempo de ejecución , porque puede hacer que las bibliotecas compartidas sean cargado junto con un programa incluso si no los necesita.
Las diferentes reglas de enlace para bibliotecas estáticas y compartidas pueden ser confuso a los programadores inexpertos, que pueden no saber si
-lfoo
en su vinculación se va a resolver a/some/where/libfoo.a
o a/some/where/libfoo.so
, y podría no entender la diferencia entre bibliotecas compartidas y estáticas Por cierto.
Esta compensación ha llevado a la situación cismática actual. Algunas distribuciones tienen cambió sus reglas de enlace GCC para bibliotecas compartidas para que cuando sea necesario el principio se aplica a todas las bibliotecas. Algunas distribuciones se han quedado con el antiguo manera.
¿Por qué sigo teniendo este problema incluso si compilo y enlace al mismo tiempo?
Si solo lo hago:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
Seguramente gcc tiene que compilar eg1.c
primero, y luego vincular el resultado
archivo objeto con libmy_lib.a
. Entonces, ¿cómo puede no saber que el archivo objeto
es necesario cuando se está haciendo el enlace?
Porque compilar y enlazar con un solo comando no cambia la orden de la secuencia de enlace.
Cuando ejecuta el comando anterior, gcc
descubre que quieres compilación +
vinculación. Así que detrás de las escenas, genera un comando de compilación, y se ejecuta
luego genera un comando de enlace, y lo ejecuta, como si usted hubiera ejecutado el
dos órdenes:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
Así que el enlace falla como lo hace si hace ejecutar esos dos comandos. El
la única diferencia que nota en el fallo es que gcc ha generado un
archivo de objeto temporal en el caso compile + link, porque no lo estás diciendo
utilizar eg1.o
. Nos véase:
/tmp/ccQk1tvs.o: In function `main'
En lugar de:
eg1.o: In function `main':
Véase también
El orden en el que se especifican las bibliotecas vinculadas interdependientes es incorrecto
Poner las bibliotecas interdependientes en el orden equivocado es solo una manera en el que puede obtener archivos que necesitan definiciones de cosas que vienen más tarde en el enlace que los archivos que proporcionan las definiciones. Poner las bibliotecas antes de la archivos objeto que se refieren a ellos es otra forma de cometiendo el mismo error.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:53
Cuando sus rutas de inclusión son diferentes
Los errores de enlazador pueden ocurrir cuando un archivo de encabezado y su biblioteca compartida asociada (.archivo lib) salir de sincronización. Déjame explicarte.
¿Cómo funcionan los enlazadores? El enlazador 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 todavía obtener un error enlazador a pesar de que la declaración y la definición parecen coincidir? Sí! Pueden tener 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:
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
Tenga en cuenta que aunque ambas declaraciones de función parecen idénticas en el código fuente, son realmente diferentes según el compilador.
Usted podría preguntarse cómo uno termina en una situación como esa? Incluye caminos ¡por supuesto! Si al compilar la biblioteca compartida, la ruta de inclusión conduce a header1.h
y termina usando header2.h
en su propio programa, se quedará rascando su encabezado preguntándose qué sucedió (juego de palabras).
Un ejemplo de cómo esto puede suceder en el mundo real se explica a continuación.
Elaboración adicional con un ejemplo
Tengo dos proyectos: graphics.lib
y main.exe
. Ambos proyectos dependen de common_math.h
. Supongamos que la biblioteca exporta la siguiente función:
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
Y luego tú sigue adelante e incluye la biblioteca en tu propio proyecto.
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
Boom! Obtienes un error de enlazador y no tienes idea de por qué está fallando. La razón es que la biblioteca común utiliza diferentes versiones de la misma include common_math.h
(lo he hecho obvio aquí en el ejemplo al incluir una ruta diferente, pero puede que no siempre sea tan obvio. Tal vez la ruta de inclusión es diferente en la configuración del compilador).
Nota en este ejemplo, el enlazador te diría que no pudo encontrar draw()
, cuando en realidad, usted sabe que obviamente está siendo exportado por la biblioteca. Podrías pasar horas rascándote la cabeza preguntándote qué salió mal. La cosa es, el enlazador 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).
Depurar el 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í: {[14]]}
- Tenga en cuenta el extraño nombre estropeado dado en el error del enlazador. (eg. draw @ graphics @ XYZ).
- Volcar los símbolos exportados de la biblioteca en un archivo de texto.
- Busque el símbolo de interés exportado y observe que el nombre mutilado es diferente.
- Pagar atención a por qué los nombres destrozados terminaron siendo diferentes. Usted sería capaz de ver que los tipos de parámetros son diferentes, a pesar de que se ven iguales en el código fuente.
- Razón por la que son diferentes. En el ejemplo anterior, son diferentes debido a diferentes archivos de inclusión.
[1] Por proyecto me refiero a un conjunto de archivos fuente que están vinculados entre sí para producir una biblioteca o un ejecutable.
EDITAR 1: Reescribió la primera sección para ser más fácil de entender. Por favor, comente a continuación para hacerme saber si algo más necesita ser arreglado. ¡Gracias!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-12 00:10:26
Falta "extern" en const
declaraciones/definiciones de variables (solo en C++)
Para las personas que vienen de C podría ser una sorpresa que en C++ global const
las variables tienen enlace interno (o estático). En C este no fue el caso, ya que todas las variables globales son implícitamente extern
(es decir, cuando falta la palabra clave static
).
Ejemplo:
// file1.cpp
const int test = 5; // in C++ same as "static const int test = 5"
int test2 = 5;
// file2.cpp
extern const int test;
extern int test2;
void foo()
{
int x = test; // linker error in C++ , no error in C
int y = test2; // no problem
}
Correcto sería usar un archivo de cabecera e incluirlo en file2.cpp y file1.cpp
extern const int test;
extern int test2;
Alternativamente uno podría declarar la variable const
en file1.cpp con extern
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-03 08:01:49
Aunque esta es una pregunta bastante antigua con múltiples respuestas aceptadas, me gustaría compartir cómo resolver un error oscuro "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 necesitaba también compilar en C++14 así que decidí usar un alias variable:
#if (defined _GLIBCXX_EXPERIMENTAL_FILESYSTEM) //is the included filesystem library experimental? (C++14 and C++17: <experimental/filesystem>)
using path_t = std::experimental::filesystem::path;
#elif (defined _GLIBCXX_FILESYSTEM) //not experimental: C++17 <filesystem>
using path_t = std::filesystem::path;
#endif
Digamos que tengo tres archivos: principal.cpp, archivo.h, file.cpp:
- archivo.h # include experimental:: filestystem > y contiene el código anterior
- archivo.cpp , la implementación del archivo.h, # incluye el archivo ".h "
- main.cpp #incluye el sistema de archivos > y el archivo ".h "
Tenga en cuenta las diferentes bibliotecas utilizadas en main.cpp y archivo.h. Desde main.cpp # incluye el archivo " .h" después de filestystem>, la versión de el sistema de archivos utilizado fue el C++17. Solía compilar el programa con los siguientes comandos:
$ g++ -g -std=c++17 -c main.cpp
-> compila main.cpp principal.o
$ g++ -g -std=c++17 -c file.cpp
-> compila el archivo.cpp y archivo.h a archivo.o
links g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> enlaces principales.o y archivo.o
De esta manera cualquier función contenida en el archivo.o y utilizado en principal.o que requerido path_t
dio errores de "referencia indefinida" porque main.o que se refiere std::filesystem::path
pero archivo.o a std::experimental::filesystem::path
.
Resolución
Para arreglar esto solo necesitaba cambiar <:filestystem> en archivo.h a
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-27 18:12:32
Al enlazar con 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 construyen con la opción -fvisibility=hidden
, solo las funciones/símbolos marcados con __attribute__ ((visibility ("default")))
son externos en el objeto compartido resultante.
Puede comprobar si los símbolos que busca son externos invocando:{[17]]}
# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL
Los símbolos ocultos/locales se muestran mediante nm
con minúsculas tipo de símbolo, por ejemplo t
en lugar de ' T para la sección de código:
nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL
También puede usar nm
con la opción -C
para desmangular los nombres (si se usó C++).
Similar a Windows-dll, uno marcaría funciones públicas con una definición, por ejemplo DLL_PUBLIC
definida como:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
Que corresponde aproximadamente a Windows ' / MSVC-version:
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
Más información sobre visibilidad se puede encontrar en la wiki de gcc.
Cuando una unidad de traducción se compila con -fvisibility=hidden
los símbolos resultantes todavía tienen enlace externo (se muestra con el tipo de símbolo en mayúsculas por nm
) y se puede utilizar para enlace externo sin problema si los archivos objeto se convierten en parte de una bibliotecas estáticas. El enlace se convierte en local solo cuando los archivos objeto están vinculados a una biblioteca compartida.
Para encontrar qué símbolos en un archivo objeto están ocultos, ejecute:
>>> objdump -t XXXX.o | grep hidden
0000000000000000 g F .text 000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g F .text 000000000000000b .hidden HIDDEN_SYMBOL2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-10 08:18:38