¿Qué es un error de referencia/símbolo externo no resuelto y cómo lo corro?

¿Cuáles son los errores no definidos de referencia/símbolos externos no resueltos? ¿Cuáles son las causas comunes, y cómo puedo corregir y prevenir estos errores?

Pregunta hecha hace 11 años, 5 meses, 2 días - Por compilercaptain


11 Respuestas:

  • Digamos que tiene el siguiente código:

    // a.cpp
    int get() { return 0; }
    
    // b.cpp
    int get(); // usually, one doesn't write this directly, but gets these
               // declarations from included header files
    int x = get();
    

    Cuando se compilan b.cpp, el compilador simplemente supone que get() símbolo definido en alguna partePero aún no le importa dónde. La fase de enlace es responsable de encontrar el símbolo y vincular correctamente los archivos del objeto producidos a partir de a.cpp y b.cpp.

    Si a.cpp no definió get, usted conseguiría un error de enlace diciendo "referencia no definida" o "símbolo externo no resuelto".

    C++ Wording estándar

    Compilar un programa C+ tiene lugar en varias fases especificadas en [lex.phases], el último de los cuales es relevante:

    9. Todas las referencias de entidad externa se resuelven. Los componentes de la biblioteca están vinculados a satisfacer referencias externas a entidades no definidas en la traducción actual. Toda la salida de este traductor se recoge en una imagen del programa que contiene información necesaria para la ejecución en su entorno de ejecución.

    See Respuesta de Keith Thompson para un resumen de estas fases.

    Los errores especificados ocurren durante esta última etapa de compilación, la más comúnmente conocida como vinculación. Básicamente significa que compilaste un montón de archivos fuente en archivos de objetos o bibliotecas, y ahora quieres que trabajen juntos.

    Errores de Linker en la práctica

    Si utiliza Microsoft Visual Studio, verá que los proyectos generan .lib archivos. Estos contienen una tabla de símbolos exportados, y una tabla de símbolos importados. Los símbolos importados se resuelven contra las bibliotecas a las que se enlaza, y los símbolos exportados se proporcionan para las bibliotecas que usan eso .lib (si la hay).

    Existen mecanismos similares para otros compiladores/ plataformas.

    Mensajes comunes de error error LNK2001, error LNK1120, error LNK2019 para Microsoft Visual Studio y undefined reference to símboloName para 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
    

    Causas comunes

    Respondida el Sep 24, 2012 a las 22:37 - por debugdynamo05ee

    Votos positivos: 0 | Votos negativos: 0

  • Miembros de clase:

    Un puro virtual El destructor necesita una implementación.

    Declarar un destructor puro todavía requiere que usted lo defina (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 es destruido implícitamente, por lo que se requiere una definición.

    virtual los métodos deben ser implementados o definidos como puros.

    Esto es similar a no-virtual métodos sin definición, con el razonamiento añadido que la declaración pura genera un dummy vtable y usted podría obtener el error de enlace 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, declarar X::foo() como puro:

    struct X
    {
        virtual void foo() = 0;
    };
    

    Non-virtual miembros de clase

    Algunos miembros deben definirse incluso si no se utilizan explícitamente:

    struct A
    { 
        ~A();
    };
    

    A continuación se produciría el error:

    A a;      //destructor undefined
    

    La implementación puede ser inline, en la definición de clase misma:

    struct A
    { 
        ~A() {}
    };
    

    o fuera:

    A::~A() {}
    

    Si la implementación está fuera de la definición de clase, pero en un encabezado, los métodos deben ser marcados como inline para prevenir una definición múltiple.

    Todos los métodos utilizados deben definirse si se utilizan.

    Un error común se olvida de 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 los datos deben definirse fuera de la clase en un 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
    

    Un inicializador puede ser proporcionado para un static const data member of integral or enumeration type within the class definition; however, odr-use of this member will still require a namespace scope definition as described above. C++11 permite la inicialización dentro de la clase para todos static const miembros de datos.

    Respondida el Sep 24, 2012 a las 22:43 - por quantumquasar

    Votos positivos: 0 | Votos negativos: 0

  • No vincularse con bibliotecas apropiadas o archivos de objetos o compilar archivos de implementación

    Comúnmente, cada unidad de traducción generará un archivo de objeto que contenga las definiciones de los símbolos definidos en esa unidad de traducción. Para usar esos símbolos, tienes que enlazar contra esos archivos de objetos.

    Under gcc especificaría todos los archivos de objetos que deben estar conectados juntos en la línea de comandos, o compilar los archivos de implementación juntos.

    g++ -o test objectFile1.o objectFile2.o -lLibraryName
    

    -l... debe ser el derecho de cualquier persona .o/.c/.cpp archivos.

    El libraryName aquí está el nombre desnudo de la biblioteca, sin adiciones específicas de la plataforma. Así que, por ejemplo, en los archivos de la biblioteca de Linux se suelen llamar libfoo.so pero sólo escribirías -lfoo. En Windows ese mismo archivo podría llamarse foo.libPero 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 -l o -L.

    Para Xcode: Agregue el Usuario Header Search Paths - confiar agregue la Biblioteca Search Path - confiar arrastrar y soltar la referencia de la biblioteca real en la carpeta del proyecto.

    Under MSVS, los archivos añadidos a un proyecto automáticamente tienen sus archivos de objetos conectados y un lib archivo se generaría (en uso común). Para usar los símbolos en un proyecto separado, necesidad de incluir el lib archivos en la configuración del proyecto. Esto se hace en la sección Linker de las propiedades del proyecto, en Input -> Additional Dependencies. (el camino hacia el lib archivo debe ser añadidos Linker -> General -> Additional Library Directories) Al utilizar una biblioteca de terceros que se proporciona con una lib archivo, la falta de hacerlo generalmente resulta en el error.

    También puede suceder que te olvides de añadir el archivo a la compilación, en cuyo caso el archivo objeto no será generado. In gcc añadiría los archivos a la línea de comandos. In MSVS añadir el archivo al proyecto lo hará compilar automáticamente (aunque los archivos pueden, manualmente, ser excluidos individualmente de la compilación).

    En la programación de Windows, el signo narrativo de que no viniste 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 debe decir qué biblioteca necesita usar. Por ejemplo, MSDN pone la información en una caja en la parte inferior de cada función en una sección llamada "Library".

    Respondida el Sep 24, 2012 a las 22:48 - por scriptsculptor

    Votos positivos: 0 | Votos negativos: 0

  • Declarado pero no definió una variable o función.

    Una declaración variable típica es

    extern int x;
    

    Como esta es sólo una declaración, una Definición individual es necesario. 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
    

    Se aplican observaciones similares 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 implemente exactamente coincida con la que declaró. Por ejemplo, usted puede tener clasificadores cv desajustados:

    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 deficiencias incluyen

    • Función/variable declarada en un espacio de nombres, definido en otro.
    • Función/variable declarada miembro de clase, definida como global (o viceversa).
    • Tipo de retorno de funciones, número de parámetro y tipos, y convención de llamadas no todos están de acuerdo exactamente.

    El mensaje de error del compilador le dará a menudo la declaración completa de la variable o función que fue declarada pero nunca definida. Compare estrechamente con la definición que proporcionó. Asegúrese de que todos los detalles coincidan.

    Respondida el Sep 24, 2012 a las 22:58 - por algorithmwizard

    Votos positivos: 0 | Votos negativos: 0

  • El orden en que se especifican las bibliotecas interdependientes vinculadas es incorrecto.

    El orden en que las bibliotecas están vinculadas DOES importa si las bibliotecas dependen una de la otra. En general, si la biblioteca A depende de la biblioteca BEntonces libA DEBE antes libB en las banderas de enlace.

    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
    

    Compile:

    $ 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 repetir otra vez, la orden DOS ¡ importa!

    Respondida el Sep 24, 2012 a las 23:07 - por byteninja8245

    Votos positivos: 0 | Votos negativos: 0

  • Los símbolos se definieron en un programa C y se utilizaron en código C++.

    La función (o variable) void foo() fue definido en un programa C y usted intenta utilizarlo en un programa C++:

    void foo();
    int main()
    {
        foo();
    }
    

    El enlace C++ espera que se manglen los nombres, por lo que tiene que declarar la función como:

    extern "C" void foo();
    int main()
    {
        foo();
    }
    

    Equivalentemente, en lugar de ser definido en un programa C, la función (o variable) void foo() se definió en C++ pero con enlace C:

    extern "C" void foo();
    

    e intenta utilizarlo en un programa C++ con enlace C++.

    Si se incluye toda una biblioteca en un archivo de cabecera (y se compiló como código C); la inclusión deberá ser la siguiente;

    extern "C" {
        #include "cheader.h"
    }
    

    Respondida el Sep 24, 2012 a las 23:13 - por algoarchitect

    Votos positivos: 0 | Votos negativos: 0

  • lo que es un "símbolo externo no definido y no resuelto"

    Intentaré explicar lo que es un "símbolo externo no definido o no resuelto".

    nota: uso g++ y Linux y todos los ejemplos es para él

    Por ejemplo, tenemos un 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 de objetos

    $ g++ -c src1.cpp -o src1.o
    $ g++ -c src2.cpp -o src2.o
    

    Después de la fase del ensamblador tenemos un archivo de objeto, que contiene cualquier símbolo a exportar. Mira los 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

    Así que, 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

    Enlace de nuestros archivos de objetos

    $ g++ src1.o src2.o -o prog
    

    y ejecutarlo

    $ ./prog
    123
    

    Linker ve símbolos exportados y lo vincula. Ahora intentamos descomponer 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 sólo construimos el archivo de objeto, el enlace no se hace todavía. Trate de 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 nombre local_var_ 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
    

    Así que, hemos visto que no hay etiquetas para el nombre local_var_, por eso Linker no lo ha encontrado. Pero somos hackers :) y podemos arreglarlo. Abra src1.s en su editor de texto y cambie

    .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
    

    i.e. 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 hemos fijado su valor a 456789. Trate de construir un archivo objeto de él

    $ g++ -c src1.s -o src2.o
    

    ok, ver salida de readelf (símbolos)

    $ readelf --symbols src1.o
    8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name
    

    local_var_name tiene Bind GLOBAL (fue LOCAL)

    enlace

    $ g++ src1.o src2.o -o prog
    

    y ejecutarlo

    $ ./prog 
    123456789
    

    Vale, lo hackearemos :)

    Por lo tanto, como resultado - un "no definido de referencia / error de símbolo externo no resuelto" ocurre cuando el linker no puede encontrar símbolos globales en los archivos del objeto.

    Respondida el Sep 24, 2012 a las 23:21 - por binarybard

    Votos positivos: 0 | Votos negativos: 0

  • Si todo lo demás falla, recompilar.

    Recientemente pude deshacerme de un error externo no resuelto en Visual Studio 2012 simplemente recompilando el archivo ofensivo. Cuando reconstruí, el error se fue.

    Esto suele ocurrir cuando dos (o más) bibliotecas tienen una dependencia cíclica. Biblioteca Un intento de utilizar símbolos en B.lib y la biblioteca B intenta utilizar símbolos de A.lib. Tampoco existen para empezar. Cuando intentas compilar A, el paso del 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-compilación Un testamento ahora funciona porque B.lib ahora se encuentra.

    Respondida el Sep 24, 2012 a las 23:28 - por debugdragon

    Votos positivos: 0 | Votos negativos: 0

  • Las implementaciones de plantilla no son visibles.

    Las plantillas no especializadas deben tener sus definiciones visibles para todas las unidades de traducción que las usen. Eso significa que no puedes separar la definición de una plantilla a un archivo de implementación. Si usted debe separar la implementación, el trabajo habitual es tener un impl archivo que incluye al final del encabezado que declara la plantilla. Una situación común es:

    template
    struct X
    {
        void foo();
    };
    
    int main()
    {
        X x;
        x.foo();
    }
    
    //differentImplementationFile.cpp
    template
    void X::foo()
    {
    }
    

    Para arreglar esto, debe mover la definición de X::foo al archivo cabecera o algún lugar visible a la unidad de traducción que lo utiliza.

    Las plantillas especializadas pueden ser implementadas en un archivo de implementación y la implementación no tiene que ser visible, pero la especialización debe ser previamente declarada.

    Para mayor explicación y otra solución posible (explicación instantánea) ver esta pregunta y respuesta.

    Respondida el Sep 24, 2012 a las 23:34 - por codecraftsman32c9

    Votos positivos: 0 | Votos negativos: 0

  • Un error de referencia/símbolo externo no resuelto se produce cuando el compilador no puede encontrar la implementación de una función, variable o clase que se está utilizando en un programa. Esto puede ocurrir cuando se llama a una función que está declarada pero no definida, o cuando se hace referencia a una variable o clase que no ha sido declarada ni definida.

    Algunas causas comunes de este tipo de errores son:

    • No incluir correctamente el archivo de cabecera donde se define la función, variable o clase.
    • No enlazar correctamente los archivos objeto que contienen las implementaciones de las funciones, variables o clases requeridas.
    • Errores de ortografía al escribir el nombre de la función, variable o clase.
    • Definir una función, variable o clase en un archivo incorrecto o en un lugar inadecuado.
    • Si se está utilizando una biblioteca externa, no agregar correctamente la biblioteca al proceso de enlace.

    Para corregir y prevenir estos errores, algunas acciones que puedes tomar son:

    • Asegurarte de incluir los archivos de cabecera necesarios en tu programa.
    1. Verificar que

    Respondida el Sep 25, 2012 a las 22:35 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Errores de Referencia/Símbolos Externos No Resueltos

    Los errores de referencia o símbolos externos no resueltos ocurren cuando el compilador o enlazador no puede encontrar una referencia a una función, variable o símbolo definido en otro módulo o biblioteca.

    Causas Comunes:

    Falta de definición: La función o símbolo no está definido en ningún lugar del programa. Inconsistencia entre archivos de cabecera y de implementación: La definición en el archivo de cabecera no coincide con la implementación en el archivo de código fuente. Falta de vinculación: El módulo que contiene la definición no está vinculado con el módulo que lo llama. Nombres de símbolos incorrectos: El nombre del símbolo en el código llamante no coincide con el nombre en la definición. Bibliotecas faltantes: La biblioteca que contiene la definición no está incluida o está vinculada incorrectamente.

    Cómo Corregir y Prevenir Errores de Referencia

    • Verifica las Definiciones: Asegúrate de que la función o símbolo esté definido en un módulo o biblioteca.
    • Compara Archivos de Cabecera e Implementación: Comprueba que el nombre, tipo y parámetros de la definición coincidan en ambos archivos.
    • Vincula los Módulos: Utiliza las opciones de vinculación adecuadas para vincular los módulos que contienen las definiciones y las llamadas.
    • Comprueba los Nombres de Símbolos: Asegúrate de que los nombres de los símbolos en las llamadas coincidan exactamente con los nombres de las definiciones.
    • Incluye Bibliotecas: Incluye las bibliotecas necesarias en el proyecto y vincula con ellas.

    Consejos para Prevenir Errores de Referencia:

    Utiliza archivos de cabecera bien mantenidos y actualizados. Asegúrate de que los módulos que contienen definiciones estén incluidos en el proyecto de compilación. Verifica constantemente los mensajes de error y advertencias del compilador o enlazador. Utiliza herramientas como grep o find para buscar definiciones de símbolos faltantes. Considera el uso de herramientas de administración de versiones como Git para rastrear y administrar los cambios en los archivos de código fuente y de cabecera.

    Respondida el Sep 25, 2012 a las 22:42 - por Gemini

    Votos positivos: 0 | Votos negativos: 0