Was ist ein undefinierter Verweis/ungelöster externer Symbolfehler und wie behebe ich ihn?

Lesezeit: 9 Minuten

Was ist ein undefinierter Verweisungeloster externer Symbolfehler und wie behebe
Luchian Grigore

Was sind undefinierte Referenz-/ungelöste externe Symbolfehler? Was sind häufige Ursachen und wie können sie behoben/verhindert werden?

  • @LuchianGrigore ‘Fühlen Sie sich frei, eine Antwort hinzuzufügen’ Ich habe es vorgezogen, den relevanten Link (IMHO) zu Ihrer primären Antwort hinzuzufügen, wenn Sie dies zulassen möchten.

    – πάντα ῥεῖ

    3. März 2014 um 22:36 Uhr

  • @jave.web: Während das passiert, bemerkt der Programmierer normalerweise, dass er keine hat this Zeiger und kein Zugriff auf Klassenmitglieder. Es kommt ziemlich selten vor, dass die Kompilierung abgeschlossen wird und nur während des Verknüpfens fehlschlägt, wenn einer nicht statischen Elementfunktion der qualifizierte Name fehlt.

    – Ben Voigt

    27. April 2015 um 22:06 Uhr

  • @jave.web: Das war genau mein Problem. Danke! Ich bin neu bei cpp, aber soweit ich das beurteilen kann, hatte ich genau das Problem, von dem Ben Voigt sagt, dass es ziemlich selten war. Ich denke, Ihre Lösung wäre eine großartige Antwort.

    – RoG

    20. Oktober 2016 um 6:42 Uhr


  • Sie können nützlich sein, ebenso wie viele Antworten auf Fragen, die als zu allgemein gekennzeichnet sind.

    – Albert van der Horst

    16. August 2019 um 19:07 Uhr

  • Ehrlich gesagt würde ich gerne ein reproduzierbares Minimalbeispiel als etwas sehen, das wir von den meisten neuen Benutzern verlangen. Ich meine nichts damit, es ist nur – wir können nicht erwarten, dass Menschen die Regeln befolgen, die wir uns nicht selbst auferlegen.

    – Danilo

    8. September 2019 um 18:54 Uhr


Was ist ein undefinierter Verweisungeloster externer Symbolfehler und wie behebe
Luchian Grigore

Klassenmitglieder:

Ein reines virtual Destruktor benötigt eine Implementierung.

Um einen Destruktor als rein zu deklarieren, müssen Sie ihn immer noch definieren (im Gegensatz zu einer regulären Funktion):

struct X
{
    virtual ~X() = 0;
};
struct Y : X
{
    ~Y() {}
};
int main()
{
    Y y;
}
//X::~X(){} //uncomment this line for successful definition

Dies geschieht, weil Destruktoren der Basisklasse aufgerufen werden, wenn das Objekt implizit zerstört wird, sodass eine Definition erforderlich ist.

virtual Methoden müssen entweder implementiert oder als rein definiert werden.

Dies ist ähnlich wie bei Nicht-virtual Methoden ohne Definition, mit der zusätzlichen Begründung, dass die reine Deklaration eine Dummy-vtable generiert und Sie möglicherweise den Linker-Fehler erhalten, ohne die Funktion zu verwenden:

struct X
{
    virtual void foo();
};
struct Y : X
{
   void foo() {}
};
int main()
{
   Y y; //linker error although there was no call to X::foo
}

Damit dies funktioniert, deklarieren Sie X::foo() als rein:

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

Nicht-virtual Klassenmitglieder

Einige Mitglieder müssen definiert werden, auch wenn sie nicht explizit verwendet werden:

struct A
{ 
    ~A();
};

Folgendes würde den Fehler liefern:

A a;      //destructor undefined

Die Implementierung kann inline in der Klassendefinition selbst erfolgen:

struct A
{ 
    ~A() {}
};

oder draußen:

A::~A() {}

Befindet sich die Implementierung außerhalb der Klassendefinition, aber in einem Header, müssen die Methoden als gekennzeichnet werden inline um eine Mehrfachdefinition zu verhindern.

Alle verwendeten Elementmethoden müssen definiert werden, wenn sie verwendet werden.

Ein häufiger Fehler ist das Vergessen, den Namen zu qualifizieren:

struct A
{
   void foo();
};

void foo() {}

int main()
{
   A a;
   a.foo();
}

Die Definition sollte sein

void A::foo() {}

static Datenmember müssen außerhalb der Klasse in a definiert werden einzelne Übersetzungseinheit:

struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x

Für a kann ein Initialisierer bereitgestellt werden static const Datenelement vom Typ Ganzzahl oder Aufzählung innerhalb der Klassendefinition; Die odr-Verwendung dieses Members erfordert jedoch weiterhin eine Namespace-Bereichsdefinition, wie oben beschrieben. C++11 ermöglicht die Initialisierung innerhalb der Klasse für alle static const Daten Mitglieder.

  • Ich dachte nur, Sie möchten vielleicht betonen, dass beides möglich ist und der dtor keine Ausnahme darstellt. (Das geht aus deiner Formulierung nicht auf den ersten Blick hervor.)

    – Deduplizierer

    20. September 2014 um 19:26 Uhr

  • Es wäre gut, wenn Sie den häufigen Fehler von explizit abdecken könnten gcc main.c anstatt gcc main.c other.c (was Anfänger oft tun, bevor ihre Projekte so groß werden, dass sie .o-Dateien erstellen) .

    – MM

    4. November 2019 um 20:43 Uhr


Was ist ein undefinierter Verweisungeloster externer Symbolfehler und wie behebe
Luchian Grigore

Deklariert, aber keine Variable oder Funktion definiert.

Eine typische Variablendeklaration ist

extern int x;

Da dies nur eine Erklärung ist, a einzelne Definition wird gebraucht. Eine entsprechende Definition wäre:

int x;

Folgendes würde beispielsweise einen Fehler erzeugen:

extern int x;
int main()
{
    x = 0;
}
//int x; // uncomment this line for successful definition

Ähnliche Bemerkungen gelten für Funktionen. Das Deklarieren einer Funktion ohne Definition führt zu folgendem Fehler:

void foo(); // declaration only
int main()
{
   foo();
}
//void foo() {} //uncomment this line for successful definition

Achten Sie darauf, dass die von Ihnen implementierte Funktion genau mit der deklarierten übereinstimmt. Beispielsweise haben Sie möglicherweise nicht übereinstimmende CV-Qualifikatoren:

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)
                          

Andere Beispiele für Nichtübereinstimmungen umfassen

  • In einem Namensraum deklarierte Funktion/Variable in einem anderen definiert.
  • Funktion/Variable als Klassenmitglied deklariert, als global definiert (oder umgekehrt).
  • Funktionsrückgabetyp, Parameternummer und -typen sowie Aufrufkonventionen stimmen nicht alle genau überein.

Die Fehlermeldung des Compilers gibt Ihnen oft die vollständige Deklaration der Variablen oder Funktion, die deklariert, aber nie definiert wurde. Vergleichen Sie es genau mit der von Ihnen angegebenen Definition. Stellen Sie sicher, dass jedes Detail übereinstimmt.

  • In VS cpp-Dateien, die denen im Header entsprechen #includes nicht zum Quellverzeichnis hinzugefügt fällt ebenfalls unter die Kategorie der fehlenden Definitionen.

    – Laurie Stearn

    30. März 2018 um 12:18 Uhr

Was ist ein undefinierter Verweisungeloster externer Symbolfehler und wie behebe
Gemeinschaft

Die Reihenfolge, in der voneinander abhängige verknüpfte Bibliotheken angegeben werden, ist falsch.

Die Reihenfolge, in der Bibliotheken verknüpft sind, spielt eine Rolle, wenn die Bibliotheken voneinander abhängen. Im Allgemeinen, wenn Bibliothek A hängt von der Bibliothek ab Bdann libA MUSS davor erscheinen libB in den Linker-Flags.

Zum Beispiel:

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

Erstellen Sie die Bibliotheken:

$ 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

Kompilieren:

$ 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

Um die Reihenfolge noch einmal zu wiederholen TUT Gegenstand!

  • In VS cpp-Dateien, die denen im Header entsprechen #includes nicht zum Quellverzeichnis hinzugefügt fällt ebenfalls unter die Kategorie der fehlenden Definitionen.

    – Laurie Stearn

    30. März 2018 um 12:18 Uhr

Was ist eine “undefinierte Referenz / nicht aufgelöstes externes Symbol”

Ich werde versuchen zu erklären, was eine “undefinierte Referenz / ein nicht aufgelöstes externes Symbol” ist.

Hinweis: Ich verwende g ++ und Linux und alle Beispiele sind dafür

Zum Beispiel haben wir einen Code

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

und

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

Erstellen Sie Objektdateien

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

Nach der Assembler-Phase haben wir eine Objektdatei, die alle zu exportierenden Symbole enthält. Betrachten Sie die Symbole

$ 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]

Ich habe einige Zeilen aus der Ausgabe verworfen, weil sie keine Rolle spielen

Wir sehen also folgende Symbole zum Exportieren.

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

src2.cpp exportiert nichts und wir haben keine seiner Symbole gesehen

Verlinken Sie unsere Objektdateien

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

und führe es aus

$ ./prog
123

Linker sieht exportierte Symbole und verknüpft sie. Jetzt versuchen wir, Zeilen in src2.cpp wie hier zu entkommentieren

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

und eine Objektdatei neu erstellen

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

OK (keine Fehler), da wir nur Objektdateien erstellen, ist die Verknüpfung noch nicht abgeschlossen. Versuche zu verlinken

$ 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

Das ist passiert, weil unser local_var_name statisch ist, dh er ist für andere Module nicht sichtbar. Jetzt tiefer. Rufen Sie die Ausgabe der Übersetzungsphase ab

$ 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

Wir haben also gesehen, dass es kein Label für local_var_name gibt, deshalb hat Linker es nicht gefunden. Aber wir sind Hacker 🙂 und wir können es reparieren. Öffnen Sie src1.s in Ihrem Texteditor und ändern Sie

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

zu

    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789

dh Sie sollten wie unten haben

    .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:
; ...

Wir haben die Sichtbarkeit von local_var_name geändert und seinen Wert auf 456789 gesetzt. Versuchen Sie, daraus eine Objektdatei zu erstellen

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

ok, siehe Readelf-Ausgabe (Symbole)

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

jetzt hat local_var_name Bind GLOBAL (war LOCAL)

Verknüpfung

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

und führe es aus

$ ./prog 
123456789

ok, wir hacken es 🙂

Infolgedessen tritt ein Fehler “undefinierter Verweis/nicht aufgelöstes externes Symbol” auf, wenn der Linker keine globalen Symbole in den Objektdateien finden kann.

1004020cookie-checkWas ist ein undefinierter Verweis/ungelöster externer Symbolfehler und wie behebe ich ihn?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy