Was sind undefinierte Referenz- / ungelöste externe Symbolfehler? Was sind häufige Ursachen und wie können sie behoben / verhindert werden?
Fühlen Sie sich frei, Ihre eigenen zu bearbeiten / hinzuzufügen.
c++
linker-errors
undefined-reference
c++-faq
unresolved-external
Luchian Grigore
quelle
quelle
Antworten:
Das Kompilieren eines C ++ - Programms erfolgt in mehreren Schritten, wie in 2.2 angegeben (Dank an Keith Thompson als Referenz) :
Die angegebenen Fehler treten in dieser letzten Phase der Kompilierung auf, die am häufigsten als Verknüpfung bezeichnet wird. Dies bedeutet im Grunde, dass Sie eine Reihe von Implementierungsdateien zu Objektdateien oder Bibliotheken kompiliert haben und diese nun zusammenarbeiten möchten.
Angenommen, Sie haben das Symbol
a
in definierta.cpp
. Nunb.cpp
erklärte dieses Symbol und benutzte es. Vor dem Verknüpfen wird einfach davon ausgegangen, dass dieses Symbol irgendwo definiert wurde , aber es ist noch egal, wo. Die Verknüpfungsphase ist dafür verantwortlich, das Symbol zu finden und korrekt zu verknüpfenb.cpp
(also tatsächlich mit dem Objekt oder der Bibliothek, die es verwendet).Wenn Sie Microsoft Visual Studio verwenden, werden Sie feststellen, dass Projekte
.lib
Dateien generieren . Diese enthalten eine Tabelle mit exportierten Symbolen und eine Tabelle mit importierten Symbolen. Die importierten Symbole werden für die Bibliotheken aufgelöst, mit denen Sie verknüpfen, und die exportierten Symbole werden für die Bibliotheken bereitgestellt, die diese verwenden.lib
(falls vorhanden).Ähnliche Mechanismen existieren für andere Compiler / Plattformen.
Häufige Fehlermeldungen
error LNK2001
,error LNK1120
,error LNK2019
für Microsoft Visual Studio undundefined reference to
symbol für GCC .Der Code:
generiert die folgenden Fehler mit GCC :
und ähnliche Fehler mit Microsoft Visual Studio :
Häufige Ursachen sind:
#pragma
(Microsoft Visual Studio)UNICODE
Definitionenquelle
Klassenmitglieder:
Ein reines
virtual
Destruktor braucht eine Implementierung.Um einen Destruktor als rein zu deklarieren, müssen Sie ihn noch definieren (im Gegensatz zu einer regulären Funktion):
Dies liegt daran, dass Basisklassendestruktoren 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 ähnelt 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:Damit dies funktioniert, deklarieren Sie
X::foo()
als rein:Nicht-
virtual
KlassenmitgliederEinige Mitglieder müssen definiert werden, auch wenn sie nicht explizit verwendet werden:
Folgendes würde den Fehler ergeben:
Die Implementierung kann in der Klassendefinition selbst inline erfolgen:
oder draußen:
Befindet sich die Implementierung außerhalb der Klassendefinition, jedoch in einem Header, müssen die Methoden so markiert werden
inline
, dass eine Mehrfachdefinition verhindert wird.Alle verwendeten Member-Methoden müssen definiert werden, wenn sie verwendet werden.
Ein häufiger Fehler ist das Vergessen, den Namen zu qualifizieren:
Die Definition sollte sein
static
Datenelemente müssen außerhalb der Klasse in einer einzigen Übersetzungseinheit definiert werden :Ein Initialisierer kann für ein
static
const
Datenelement vom Integral- oder Aufzählungstyp innerhalb der Klassendefinition bereitgestellt werden . Für die Verwendung dieses Mitglieds ist jedoch weiterhin eine Definition des Namespace-Bereichs erforderlich, wie oben beschrieben. C ++ 11 ermöglicht die Initialisierung innerhalb der Klasse für allestatic const
Datenelemente.quelle
Fehler beim Verknüpfen mit geeigneten Bibliotheken / Objektdateien oder Kompilieren von Implementierungsdateien
Üblicherweise generiert jede Übersetzungseinheit eine Objektdatei, die die Definitionen der in dieser Übersetzungseinheit definierten Symbole enthält. Um diese Symbole zu verwenden, müssen Sie eine Verknüpfung zu diesen Objektdateien herstellen.
Unter gcc würden Sie alle Objektdateien angeben, die in der Befehlszeile miteinander verknüpft werden sollen, oder die Implementierungsdateien zusammen kompilieren.
Das
libraryName
hier ist nur der bloße Name der Bibliothek ohne plattformspezifische Ergänzungen. So werden zB unter Linux normalerweise Bibliotheksdateien aufgerufen,libfoo.so
aber Sie würden nur schreiben-lfoo
. Unter Windows wird möglicherweise dieselbe Datei aufgerufenfoo.lib
, Sie verwenden jedoch dasselbe Argument. Möglicherweise müssen Sie das Verzeichnis hinzufügen, in dem diese Dateien gefunden werden können-L‹directory›
. Stellen Sie sicher, dass nach-l
oder kein Leerzeichen steht-L
.Für XCode : Fügen Sie die Benutzerkopf-Suchpfade hinzu -> fügen Sie den Bibliothekssuchpfad hinzu -> ziehen Sie die tatsächliche Bibliotheksreferenz per Drag & Drop in den Projektordner.
Unter MSVS werden Dateien, die einem Projekt hinzugefügt wurden, automatisch mit ihren Objektdateien verknüpft, und es wird eine
lib
Datei generiert (im allgemeinen Sprachgebrauch). Um die Symbole in einem separaten Projekt zu verwenden, müssen Sie dielib
Dateien in die Projekteinstellungen aufnehmen. Dies erfolgt im Abschnitt Linker der Projekteigenschaften inInput -> Additional Dependencies
. (Der Pfad zurlib
Datei sollte in hinzugefügt werden.Linker -> General -> Additional Library Directories
) Wenn Sie eine Drittanbieter-Bibliothek verwenden, die mit einem versehen istlib
Datei wird, führt dies normalerweise zu einem Fehler.Es kann auch vorkommen, dass Sie vergessen, die Datei zur Kompilierung hinzuzufügen. In diesem Fall wird die Objektdatei nicht generiert. In gcc würden Sie die Dateien zur Befehlszeile hinzufügen. In MSVS die Datei Hinzufügen zum Projekt automatisch kompiliert (obwohl Dateien manuell einzeln vom Build ausgeschlossen werden können).
In der Windows-Programmierung ist das verräterische Zeichen dafür, dass Sie keine erforderliche Bibliothek verknüpft haben, dass der Name des nicht aufgelösten Symbols mit beginnt
__imp_
. Suchen Sie in der Dokumentation nach dem Namen der Funktion und geben Sie an, welche Bibliothek Sie verwenden müssen. Beispielsweise fügt MSDN die Informationen in ein Feld am Ende jeder Funktion in einem Abschnitt namens "Bibliothek" ein.quelle
gcc main.c
statt behandeln könntengcc main.c other.c
(was Anfänger oft tun, bevor ihre Projekte so groß werden, dass sie .o-Dateien erstellen).Deklariert, aber keine Variable oder Funktion definiert.
Eine typische Variablendeklaration ist
Da dies nur eine Deklaration ist, ist eine einzige Definition erforderlich. Eine entsprechende Definition wäre:
Folgendes würde beispielsweise einen Fehler erzeugen:
Ähnliche Bemerkungen gelten für Funktionen. Das Deklarieren einer Funktion ohne sie zu definieren führt zu dem Fehler:
Achten Sie darauf, dass die von Ihnen implementierte Funktion genau mit der von Ihnen deklarierten übereinstimmt. Beispielsweise haben Sie möglicherweise nicht übereinstimmende Lebenslaufqualifizierer:
Andere Beispiele für Fehlpaarungen sind
Die Fehlermeldung des Compilers gibt Ihnen häufig die vollständige Deklaration der Variablen oder Funktion, die deklariert, aber nie definiert wurde. Vergleichen Sie es eng mit der von Ihnen angegebenen Definition. Stellen Sie sicher, dass jedes Detail übereinstimmt.
quelle
#includes
nicht zum Quellverzeichnis hinzugefügt wurden , ebenfalls unter die Kategorie der fehlenden Definitionen.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. Wenn die Bibliothek
A
von der Bibliothek abhängtB
,libA
MUSS sielibB
im Allgemeinen zuvor in den Linker-Flags angezeigt werden.Zum Beispiel:
Erstellen Sie die Bibliotheken:
Kompilieren:
Um es noch einmal zu wiederholen, die Reihenfolge ist wichtig!
quelle
Was ist ein "undefinierter Verweis / ungelöstes externes Symbol"?
Ich werde versuchen zu erklären, was ein "undefinierter Verweis / ungelöstes externes Symbol" ist.
Zum Beispiel haben wir einen Code
und
Objektdateien erstellen
Nach der Assembler-Phase haben wir eine Objektdatei, die alle zu exportierenden Symbole enthält. Schau dir die Symbole an
Ich habe einige Zeilen von der Ausgabe abgelehnt, weil sie keine Rolle spielen
Wir sehen also folgende Symbole zum Exportieren.
src2.cpp exportiert nichts und wir haben keine seiner Symbole gesehen
Verknüpfen Sie unsere Objektdateien
und starte es
Linker sieht exportierte Symbole und verknüpft sie. Jetzt versuchen wir, Zeilen in src2.cpp wie hier zu kommentieren
und erstellen Sie eine Objektdatei neu
OK (keine Fehler), da wir nur Objektdateien erstellen, ist die Verknüpfung noch nicht abgeschlossen. Versuchen Sie zu verlinken
Dies ist passiert, weil unser local_var_name statisch ist, dh für andere Module nicht sichtbar ist. Jetzt tiefer. Holen Sie sich die Ausgabe der Übersetzungsphase
Wir haben also gesehen, dass es keine Bezeichnung für local_var_name gibt. Deshalb hat Linker sie nicht gefunden. Aber wir sind Hacker :) und wir können es beheben. Öffnen Sie src1.s in Ihrem Texteditor und ändern Sie es
zu
dh du solltest wie unten haben
Wir haben die Sichtbarkeit von local_var_name geändert und den Wert auf 456789 festgelegt. Versuchen Sie, daraus eine Objektdatei zu erstellen
ok, siehe Selbstausgabe (Symbole)
jetzt hat local_var_name Bind GLOBAL (war LOCAL)
Verknüpfung
und starte es
ok, wir hacken es :)
Infolgedessen tritt ein "undefinierter Referenz- / ungelöster externer Symbolfehler" auf, wenn der Linker keine globalen Symbole in den Objektdateien finden kann.
quelle
Symbole wurden in einem C-Programm definiert und in C ++ - Code verwendet.
Die Funktion (oder Variable)
void foo()
wurde in einem C-Programm definiert und Sie versuchen, sie in einem C ++ - Programm zu verwenden:Der C ++ - Linker erwartet, dass Namen entstellt werden. Daher müssen Sie die Funktion wie folgt deklarieren:
Entsprechend wurde die Funktion (oder Variable) nicht in einem C-Programm
void foo()
definiert, sondern in C ++, jedoch mit C-Verknüpfung:und Sie versuchen, es in einem C ++ - Programm mit C ++ - Verknüpfung zu verwenden.
Wenn eine gesamte Bibliothek in einer Header-Datei enthalten ist (und als C-Code kompiliert wurde); Das Include muss wie folgt sein:
quelle
#ifdef __cplusplus [\n] extern"C" { [\n] #endif
und umgeben#ifdef __cplusplus [\n] } [\n] #endif
(dies[\n]
ist ein echter Wagenrücklauf, aber ich kann dies nicht richtig in einen Kommentar schreiben).extern "C" { #include <myCppHeader.h> }
.Wenn alles andere fehlschlägt, kompilieren Sie neu.
Ich konnte kürzlich einen ungelösten externen Fehler in Visual Studio 2012 beseitigen, indem ich die fehlerhafte Datei neu kompilierte. Als ich wieder aufgebaut habe, ist der Fehler verschwunden.
Dies geschieht normalerweise, wenn zwei (oder mehr) Bibliotheken eine zyklische Abhängigkeit aufweisen. Bibliothek A versucht, Symbole in B.lib zu verwenden, und Bibliothek B versucht, Symbole aus A.lib zu verwenden. Beides existiert nicht, um damit zu beginnen. Wenn Sie versuchen, A zu kompilieren, schlägt der Verknüpfungsschritt fehl, da B.lib nicht gefunden werden kann. A.lib wird generiert, aber keine DLL. Anschließend kompilieren Sie B, was erfolgreich sein wird, und generieren B.lib. Das erneute Kompilieren von A funktioniert jetzt, da B.lib jetzt gefunden wird.
quelle
Fehler beim Importieren / Exportieren von Methoden / Klassen über Module / DLL (compilerspezifisch).
MSVS erfordert , dass Sie , welche Symbole auf den Export und Import angeben Verwendung
__declspec(dllexport)
und__declspec(dllimport)
.Diese doppelte Funktionalität wird normalerweise durch die Verwendung eines Makros erreicht:
Das Makro wird
THIS_MODULE
nur in dem Modul definiert, das die Funktion exportiert. Auf diese Weise lautet die Erklärung:erweitert sich auf
und weist den Compiler an, die Funktion zu exportieren, da das aktuelle Modul seine Definition enthält. Wenn die Deklaration in ein anderes Modul aufgenommen wird, wird sie auf erweitert
und teilt dem Compiler mit, dass sich die Definition in einer der Bibliotheken befindet, mit denen Sie verknüpft haben (siehe auch 1). ).
Sie können ähnliche Import- / Exportklassen verwenden:
quelle
visibility
die.def
Dateien von GCC und Windows erwähnt werden, da diese auch den Symbolnamen und die Präsenz beeinflussen..def
Dateien mehr verwendet . Fühlen Sie sich frei, eine Antwort hinzuzufügen oder diese zu bearbeiten.Dies ist eine der verwirrendsten Fehlermeldungen, die jeder VC ++ - Programmierer immer wieder gesehen hat. Lassen Sie uns zuerst die Dinge klarer machen.
A. Was ist ein Symbol? Kurz gesagt, ein Symbol ist ein Name. Dies kann ein Variablenname, ein Funktionsname, ein Klassenname, ein Typedef-Name oder etwas anderes sein als die Namen und Zeichen, die zur C ++ - Sprache gehören. Es wird benutzerdefiniert oder von einer Abhängigkeitsbibliothek (einer anderen benutzerdefinierten Bibliothek) eingeführt.
B. Was ist extern? In VC ++ wird jede Quelldatei (.cpp, .c usw.) Als Übersetzungseinheit betrachtet, der Compiler kompiliert jeweils eine Einheit und generiert eine Objektdatei (.obj) für die aktuelle Übersetzungseinheit. (Beachten Sie, dass jede Header-Datei, die in dieser Quelldatei enthalten ist, vorverarbeitet wird und als Teil dieser Übersetzungseinheit betrachtet wird.) Alles innerhalb einer Übersetzungseinheit wird als intern betrachtet, alles andere wird als extern betrachtet. In C ++ können Sie auf ein externes Symbol verweisen, indem Sie Schlüsselwörter wie
extern
:__declspec (dllimport)
und so weiter.C. Was ist "Entschlossenheit"? Auflösen ist ein Verknüpfungszeitbegriff. In der Verknüpfungszeit versucht der Linker, die externe Definition für jedes Symbol in Objektdateien zu finden, die seine Definition nicht intern finden können. Der Umfang dieses Suchvorgangs umfasst:
Dieser Suchvorgang wird als Auflösung bezeichnet.
D. Warum schließlich ungelöstes externes Symbol? Wenn der Linker die externe Definition für ein Symbol ohne interne Definition nicht finden kann, meldet er einen Fehler mit einem nicht aufgelösten externen Symbol.
E. Mögliche Ursachen für LNK2019 : Ungelöster externer Symbolfehler . Wir wissen bereits, dass dieser Fehler darauf zurückzuführen ist, dass der Linker die Definition externer Symbole nicht gefunden hat. Die möglichen Ursachen können wie folgt sortiert werden:
Wenn wir zum Beispiel eine Funktion namens foo haben, die in a.cpp definiert ist:
In b.cpp wollen wir die Funktion foo aufrufen, also fügen wir hinzu
Um die Funktion foo () zu deklarieren und in einem anderen Funktionskörper aufzurufen, sagen Sie
bar()
:Wenn Sie diesen Code erstellen, wird ein LNK2019-Fehler angezeigt, der sich darüber beschwert, dass foo ein ungelöstes Symbol ist. In diesem Fall wissen wir, dass foo () seine Definition in a.cpp hat, sich jedoch von der von uns aufgerufenen unterscheidet (anderer Rückgabewert). Dies ist der Fall, wenn eine Definition existiert.
Wenn wir einige Funktionen in einer Bibliothek aufrufen möchten, die Importbibliothek jedoch nicht zur zusätzlichen Abhängigkeitsliste (festgelegt von :)
Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
Ihrer Projekteinstellung hinzugefügt wird . Jetzt meldet der Linker einen LNK2019, da die Definition im aktuellen Suchbereich nicht vorhanden ist.quelle
Vorlagenimplementierungen nicht sichtbar.
Bei nicht spezialisierten Vorlagen müssen die Definitionen für alle Übersetzungseinheiten sichtbar sein, die sie verwenden. Das heißt, Sie können die Definition einer Vorlage nicht von einer Implementierungsdatei trennen. Wenn Sie die Implementierung trennen müssen, besteht die übliche Problemumgehung darin, eine
impl
Datei am Ende des Headers anzugeben, der die Vorlage deklariert. Eine häufige Situation ist:Um dies zu beheben, müssen Sie die Definition von verschieben
X::foo
in die Header-Datei oder an eine Stelle verschieben, die für die Übersetzungseinheit, die sie verwendet, sichtbar ist.Spezialisierte Vorlagen können in einer Implementierungsdatei implementiert werden und die Implementierung muss nicht sichtbar sein, aber die Spezialisierung muss zuvor deklariert werden.
Weitere Erklärungen und eine andere mögliche Lösung (explizite Instanziierung) finden Sie in dieser Frage und Antwort .
quelle
undefinierter Verweis auf
WinMain@16
oder ähnliche 'ungewöhnliche'main()
Einstiegspunktreferenz (insbesondere fürVisual-Studio).Möglicherweise haben Sie es versäumt, den richtigen Projekttyp mit Ihrer tatsächlichen IDE auszuwählen. Die IDE möchte möglicherweise z. B. Windows-Anwendungsprojekte anstelle der häufig verwendeten
int main(int argc, char** argv);
Signatur an eine solche Einstiegspunktfunktion (wie in der fehlenden Referenz oben angegeben) binden .Wenn Ihre IDE Plain Console-Projekte unterstützt, möchten Sie möglicherweise diesen Projekttyp anstelle eines Windows-Anwendungsprojekts auswählen.
Hier werden case1 und case2 anhand eines realen Problems ausführlicher behandelt .
quelle
WinMain
. Gültige C ++ - Programme benötigen amain
.Wenn Sie Bibliotheken von Drittanbietern verwenden, stellen Sie sicher, dass Sie über die richtigen 32/64-Bit-Binärdateien verfügen
quelle
Microsoft bietet eine Möglichkeit,
#pragma
zum Zeitpunkt der Verknüpfung auf die richtige Bibliothek zu verweisen.Zusätzlich zum Bibliothekspfad einschließlich des Verzeichnisses der Bibliothek sollte dies der vollständige Name der Bibliothek sein.
quelle
Das Visual Studio NuGet-Paket muss für die neue Toolset-Version aktualisiert werden
Ich hatte gerade dieses Problem beim Versuch, libpng mit Visual Studio 2013 zu verknüpfen. Das Problem ist, dass die Paketdatei nur Bibliotheken für Visual Studio 2010 und 2012 enthielt.
Die richtige Lösung besteht darin, zu hoffen, dass der Entwickler ein aktualisiertes Paket veröffentlicht und dann aktualisiert. Für mich hat es jedoch funktioniert, indem eine zusätzliche Einstellung für VS2013 gehackt wurde, die auf die VS2012-Bibliotheksdateien verweist.
Ich habe das Paket (im
packages
Ordner im Verzeichnis der Lösung) bearbeitet, indem ichpackagename\build\native\packagename.targets
in dieser Datei allev110
Abschnitte gefunden und kopiert habe . Ich änderte diev110
aufv120
in den Bedingungsfelder nur sehr vorsichtig die Dateipfade alle zu verlassenv110
. Dadurch konnte Visual Studio 2013 einfach eine Verknüpfung zu den Bibliotheken für 2012 herstellen, und in diesem Fall funktionierte es.quelle
Angenommen, Sie haben ein großes Projekt in C ++ geschrieben, das tausend CPP-Dateien und tausend H-Dateien enthält. Nehmen wir an, das Projekt hängt auch von zehn statischen Bibliotheken ab. Nehmen wir an, wir arbeiten unter Windows und erstellen unser Projekt in Visual Studio 20xx. Wenn Sie Strg + F7 Visual Studio drücken, um mit dem Kompilieren der gesamten Lösung zu beginnen (nehmen wir an, wir haben nur ein Projekt in der Lösung).
Was bedeutet Zusammenstellung?
Der zweite Schritt der Kompilierung wird von Linker ausgeführt. Linker sollte die gesamte Objektdatei zusammenführen und schließlich die Ausgabe erstellen (die eine ausführbare Datei oder eine Bibliothek sein kann).
Schritte zum Verknüpfen eines Projekts
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
Überwachung
So lösen Sie diese Art von Fehler
Compiler-Zeitfehler:
Linker-Zeitfehler
#pragma once
Sie diese Option, um dem Compiler zu erlauben, keinen Header einzuschließen, wenn dieser bereits in der aktuellen kompilierten CPP enthalten warquelle
Ein Fehler im Compiler / in der IDE
Ich hatte kürzlich dieses Problem und es stellte sich heraus, dass es ein Fehler in Visual Studio Express 2013 war . Ich musste eine Quelldatei aus dem Projekt entfernen und erneut hinzufügen, um den Fehler zu beheben.
Schritte, um zu versuchen, wenn Sie glauben, dass es ein Fehler in Compiler / IDE sein könnte:
quelle
Verwenden Sie den Linker, um den Fehler zu diagnostizieren
Die meisten modernen Linker enthalten eine ausführliche Option, die in unterschiedlichem Maße gedruckt wird.
Für gcc und clang; Sie würden normalerweise
-v -Wl,--verbose
oder-v -Wl,-v
zur Befehlszeile hinzufügen . Weitere Details finden Sie hier;Bei MSVC wird
/VERBOSE
(insbesondere/VERBOSE:LIB
) der Link-Befehlszeile hinzugefügt./VERBOSE
Linker-Option .quelle
Die verknüpfte LIB-Datei ist einer DLL zugeordnet
Ich hatte das gleiche Problem. Angenommen, ich habe Projekte MyProject und TestProject. Ich hatte die lib-Datei für MyProject effektiv mit dem TestProject verknüpft. Diese lib-Datei wurde jedoch erstellt, als die DLL für das MyProject erstellt wurde. Außerdem enthielt ich nicht für alle Methoden im MyProject Quellcode, sondern nur Zugriff auf die Einstiegspunkte der DLL.
Um das Problem zu lösen, habe ich das MyProject als LIB erstellt und TestProject mit dieser LIB-Datei verknüpft (ich kopiere die generierte LIB-Datei und füge sie in den TestProject-Ordner ein). Ich kann dann MyProject erneut als DLL erstellen. Es wird kompiliert, da die Bibliothek, mit der TestProject verknüpft ist, Code für alle Methoden in Klassen in MyProject enthält.
quelle
Da die Leute bei Linkerfehlern auf diese Frage gerichtet zu sein scheinen, werde ich dies hier hinzufügen.
Ein möglicher Grund für Linkerfehler mit GCC 5.2.0 ist, dass jetzt standardmäßig eine neue libstdc ++ - Bibliothek ABI ausgewählt ist.
Wenn Sie also beim Wechsel zu einem GCC nach 5.1.0 plötzlich Linkerfehler erhalten, sollten Sie dies überprüfen.
quelle
Ein Wrapper um GNU ld, der keine Linker-Skripte unterstützt
Einige .so-Dateien sind tatsächlich GNU ld-Linker-Skripte , z. B. ist die Datei libtbb.so eine ASCII-Textdatei mit folgendem Inhalt:
Einige komplexere Builds unterstützen dies möglicherweise nicht. Wenn Sie beispielsweise -v in die Compileroptionen aufnehmen, können Sie sehen, dass der Mainwin-Gcc-Wrapper mwdip Linker-Skriptbefehlsdateien in der ausführlichen Ausgabeliste der zu verknüpfenden Bibliotheken verwirft. Eine einfache Lösung besteht darin, den Linker-Skript-Eingabebefehl zu ersetzen Datei mit einer Kopie der Datei (oder einem Symlink), z
Oder Sie können das Argument -l durch den vollständigen Pfad des .so ersetzen, z. B. anstelle von
-ltbb
do/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
quelle
Ihre Verknüpfung belegt Bibliotheken vor den Objektdateien, die auf sie verweisen
libfoo
ankommtlibbar
, dann setzt Ihre Verknüpfung richtiglibfoo
vorlibbar
.undefined reference to
etwas Fehlern.#include
d und sind in der Tat in den Bibliotheken definiert , die Sie verknüpfen.Beispiele sind in C. Sie könnten genauso gut C ++ sein
Ein minimales Beispiel für eine statische Bibliothek, die Sie selbst erstellt haben
my_lib.c
my_lib.h
eg1.c
Sie erstellen Ihre statische Bibliothek:
Sie kompilieren Ihr Programm:
Sie versuchen es zu verknüpfen
libmy_lib.a
und scheitern:Das gleiche Ergebnis, wenn Sie in einem Schritt kompilieren und verknüpfen, wie:
Ein minimales Beispiel für eine gemeinsam genutzte Systembibliothek ist die Komprimierungsbibliothek
libz
eg2.c
Kompilieren Sie Ihr Programm:
Versuchen Sie, Ihr Programm mit zu verknüpfen,
libz
und schlagen Sie fehl:Gleiches gilt, wenn Sie auf einmal kompilieren und verknüpfen:
Und eine Variation von Beispiel 2 mit
pkg-config
:Was machst du falsch
In der Reihenfolge der Objektdateien und Bibliotheken, die Sie verknüpfen möchten, um Ihr Programm zu erstellen, platzieren Sie die Bibliotheken vor den Objektdateien, die auf sie verweisen. Sie müssen die Bibliotheken nach platzieren den Objektdateien platzieren, die auf sie verweisen.
Beispiel 1 richtig verknüpfen:
Erfolg:
Beispiel 2 richtig verknüpfen:
Erfolg:
Verknüpfen Sie die
pkg-config
Variante von Beispiel 2 richtig:Die Erklärung
Das Lesen ist ab hier optional .
Standardmäßig verbraucht ein von GCC in Ihrer Distribution generierter Verknüpfungsbefehl die Dateien in der Verknüpfung von links nach rechts in der Befehlszeilenreihenfolge. Wenn festgestellt wird, dass eine Datei auf etwas verweist und keine Definition dafür enthält, wird in Dateien weiter rechts nach einer Definition gesucht. Wenn es schließlich eine Definition findet, wird die Referenz aufgelöst. Wenn Referenzen am Ende ungelöst bleiben, schlägt die Verknüpfung fehl: Der Linker sucht nicht rückwärts.
Zunächst Beispiel 1 mit statischer Bibliothek
my_lib.a
Eine statische Bibliothek ist ein indiziertes Archiv von Objektdateien. Wenn der Linker
-lmy_lib
in der Verknüpfungssequenz feststellt , dass sich dies auf die statische Bibliothek bezieht./libmy_lib.a
, möchte er wissen, ob Ihr Programm eine der Objektdateien in benötigtlibmy_lib.a
.Es gibt nur Objektdatei in
libmy_lib.a
, nämlichmy_lib.o
, und es gibt nur eine Sache der Definition inmy_lib.o
, nämlich die Funktionhw
.Der Linker entscheidet, dass Ihr Programm
my_lib.o
genau dann benötigt, wenn er bereits weiß, dass Ihr Programm aufhw
eine oder mehrere der Objektdateien verweist , die es bereits zum Programm hinzugefügt hat, und dass keine der Objektdateien, die es bereits hinzugefügt hat, eine enthält Definition fürhw
.Wenn dies zutrifft, extrahiert der Linker eine Kopie
my_lib.o
aus der Bibliothek und fügt sie Ihrem Programm hinzu. Dann enthält das Programm eine Definition fürhw
, so seine Verweise aufhw
sind aufgelöst .Wenn Sie versuchen, das Programm wie folgt zu verknüpfen:
Der Linker hat
eg1.o
dem Programm nichts hinzugefügt , wenn er es sieht-lmy_lib
. Denn zu diesem Zeitpunkt hat es nicht geseheneg1.o
. Ihr Programm macht noch keine Verweise aufhw
: es hat noch keine Referenzen machen überhaupt , denn alle Referenzen macht es in sindeg1.o
.Der Linker fügt sich also nicht
my_lib.o
in das Programm ein und hat keine weitere Verwendung fürlibmy_lib.a
.Als nächstes wird es gefunden
eg1.o
und als Programm hinzugefügt. Eine Objektdatei in der Verknüpfungssequenz wird immer zum Programm hinzugefügt. Das Programm verweist nun aufhw
und enthält keine Definition vonhw
; In der Verknüpfungssequenz ist jedoch nichts mehr vorhanden, was die fehlende Definition liefern könnte. Der Verweis aufhw
wird ungelöst und die Verknüpfung schlägt fehl.Zweitens Beispiel 2 mit gemeinsam genutzter Bibliothek
libz
Eine gemeinsam genutzte Bibliothek ist kein Archiv von Objektdateien oder Ähnlichem. Es ist viel mehr wie ein Programm , das keine
main
Funktion hat und stattdessen mehrere andere Symbole verfügbar macht, die es definiert, damit andere Programme sie zur Laufzeit verwenden können.Viele Linux - Distributionen heute konfigurieren ihre GCC Toolchain , so dass seine Sprache Treiber (
gcc
,g++
,gfortran
usw.) anweisen , das System - Linker (ld
) zu Link Shared Libraries auf einer as-needed Basis. Sie haben eine dieser Distributionen.Dies bedeutet, dass der Linker, wenn er
-lz
in der Verknüpfungssequenz findet und herausfindet, dass sich dies auf die gemeinsam genutzte Bibliothek bezieht (z. B.)/usr/lib/x86_64-linux-gnu/libz.so
, wissen möchte, ob Referenzen, die er zu Ihrem Programm hinzugefügt hat und die noch nicht definiert sind, Definitionen haben exportiert vonlibz
Wenn das wahr ist, dann wird der Linker nicht kopieren Sie alle Stücke aus
libz
und fügen Sie sie in Ihrem Programm; Stattdessen wird nur der Code Ihres Programms behandelt, so dass: -Zur Laufzeit
libz
lädt der Systemprogrammlader eine Kopie von in denselben Prozess wie Ihr Programm, wenn er eine Kopie Ihres Programms lädt, um es auszuführen.Wenn Ihr Programm zur Laufzeit auf etwas verweist, das in definiert ist
libz
, verwendet diese Referenz die Definition, die von der Kopie vonlibz
im selben Prozess exportiert wurde .Ihr Programm möchte sich nur auf eine Sache beziehen, von der eine Definition exportiert wurde
libz
, nämlich die FunktionzlibVersion
, auf die nur einmal in verwiesen wirdeg2.c
. Wenn der Linker diesen Verweis zu Ihrem Programm hinzufügt und dann die von exportierte Definition findetlibz
, wird der Verweis aufgelöstAber wenn Sie versuchen, das Programm wie folgt zu verlinken:
Die Reihenfolge der Ereignisse ist genauso falsch wie in Beispiel 1. Zu dem Zeitpunkt, an dem der Linker feststellt
-lz
, gibt es keine Verweise auf irgendetwas im Programm: Sie sind alle ineg2.o
, was noch nicht gesehen wurde. Der Linker entscheidet also, dass er keine Verwendung hatlibz
. Wenn es erreicht isteg2.o
, es dem Programm hinzufügt und dann einen undefinierten Verweis auf hatzlibVersion
, ist die Verknüpfungssequenz beendet; Diese Referenz ist nicht aufgelöst und die Verknüpfung schlägt fehl.Schließlich hat die
pkg-config
Variation von Beispiel 2 eine jetzt offensichtliche Erklärung. Nach der Schalenerweiterung:wird:
Das ist wieder nur Beispiel 2.
Ich kann das Problem in Beispiel 1 reproduzieren, aber nicht in Beispiel 2
Die Verknüpfung:
funktioniert gut für dich!
(Oder: Diese Verknüpfung hat beispielsweise bei Fedora 23 gut funktioniert, schlägt jedoch unter Ubuntu 16.04 fehl.)
Dies liegt daran, dass die Distribution, in der die Verknüpfung funktioniert, eine der Distributionen ist, die ihre GCC-Toolchain nicht so konfiguriert, dass gemeinsam genutzte Bibliotheken nach Bedarf verknüpft werden .
Früher war es für Unix-ähnliche Systeme normal, statische und gemeinsam genutzte Bibliotheken nach unterschiedlichen Regeln zu verknüpfen. Statische Bibliotheken in einer Verknüpfungssequenz wurden nach Bedarf in Beispiel 1 erläutert, gemeinsam genutzte Bibliotheken wurden jedoch unbedingt verknüpft.
Dieses Verhalten ist zur Geschäftszeit wirtschaftlich, da der Linker nicht darüber nachdenken muss, ob das Programm eine gemeinsam genutzte Bibliothek benötigt: Wenn es sich um eine gemeinsam genutzte Bibliothek handelt, verknüpfen Sie sie. Und die meisten Bibliotheken in den meisten Verknüpfungen sind gemeinsam genutzte Bibliotheken. Aber es gibt auch Nachteile:
Es ist zur Laufzeit unwirtschaftlich , da es dazu führen kann, dass gemeinsam genutzte Bibliotheken zusammen mit einem Programm geladen werden, auch wenn sie nicht benötigt werden.
Die unterschiedlichen Verknüpfungsregeln für statische und gemeinsam genutzte Bibliotheken können für unerfahrene Programmierer verwirrend sein, die möglicherweise nicht wissen, ob sich
-lfoo
ihre Verknüpfung zu/some/where/libfoo.a
oder zu auflösen wird/some/where/libfoo.so
, und den Unterschied zwischen gemeinsam genutzten und statischen Bibliotheken möglicherweise ohnehin nicht verstehen.Dieser Kompromiss hat heute zu einer schismatischen Situation geführt. Einige Distributionen haben ihre GCC-Verknüpfungsregeln für gemeinsam genutzte Bibliotheken so geändert, dass das Prinzip nach Bedarf für alle Bibliotheken gilt. Einige Distributionen haben sich an den alten Weg gehalten.
Warum bekomme ich dieses Problem immer noch, auch wenn ich gleichzeitig kompiliere und verlinke?
Wenn ich es nur tue:
Natürlich muss gcc
eg1.c
zuerst kompilieren und dann die resultierende Objektdatei mit verknüpfenlibmy_lib.a
. Wie kann es also nicht wissen, dass beim Verknüpfen eine Objektdatei benötigt wird?Da das Kompilieren und Verknüpfen mit einem einzelnen Befehl die Reihenfolge der Verknüpfungssequenz nicht ändert.
Wenn Sie den obigen Befehl
gcc
ausführen, stellen Sie fest, dass Sie Kompilierung + Verknüpfung wünschen. Hinter den Kulissen wird ein Kompilierungsbefehl generiert und ausgeführt. Anschließend wird ein Verknüpfungsbefehl generiert und ausgeführt, als hätten Sie die beiden Befehle ausgeführt:So ist die Verknüpfung nicht nur , wie es funktioniert , wenn Sie tun , diese beiden Befehle ausführen. Der einzige Unterschied, den Sie bei dem Fehler feststellen, besteht darin, dass gcc im Fall compile + link eine temporäre Objektdatei generiert hat, da Sie nicht die Verwendung anweisen
eg1.o
. Wir sehen:anstatt:
Siehe auch
Die Reihenfolge, in der voneinander abhängige verknüpfte Bibliotheken angegeben werden, ist falsch
Das Versetzen von voneinander abhängigen Bibliotheken in die falsche Reihenfolge ist nur eine Möglichkeit, Dateien abzurufen, für die Definitionen von Dingen erforderlich sind, die später in der Verknüpfung erscheinen als die Dateien, die die Definitionen bereitstellen . Das Platzieren von Bibliotheken vor den Objektdateien, die auf sie verweisen, ist eine weitere Möglichkeit, denselben Fehler zu machen.
quelle
Befreundende Vorlagen ...
Gegeben das Code-Snippet eines Vorlagentyps mit einem Freund-Operator (oder einer Friend-Funktion);
Das
operator<<
wird als Nicht-Vorlagenfunktion deklariert. Für jeden Typ,T
der mit verwendet wirdFoo
, muss eine Vorlage ohne Vorlage vorhanden seinoperator<<
. Wenn beispielsweise ein TypFoo<int>
deklariert ist, muss eine Operatorimplementierung wie folgt vorhanden sein.Da es nicht implementiert ist, kann der Linker es nicht finden und führt zu dem Fehler.
Um dies zu korrigieren, können Sie einen Vorlagenoperator vor dem
Foo
Typ deklarieren und dann als Freund die entsprechende Instanziierung deklarieren. Die Syntax ist etwas umständlich, sieht aber wie folgt aus:Der obige Code beschränkt die Freundschaft des Betreibers auf die entsprechende Instanziierung von
Foo
, dh dieoperator<< <int>
Instanziierung ist auf den Zugriff auf die privaten Mitglieder der Instanziierung von beschränktFoo<int>
.Alternativen umfassen;
Zulassen, dass sich die Freundschaft wie folgt auf alle Instanziierungen der Vorlagen erstreckt;
Oder die Implementierung für
operator<<
kann inline innerhalb der Klassendefinition erfolgen.Beachten Sie , dass, wenn die Deklaration des Operators (oder der Funktion) nur in der Klasse angezeigt wird, der Name nicht für die "normale" Suche verfügbar ist, sondern nur für die argumentabhängige Suche von cppreference .
Es gibt weitere Informationen zu Vorlagenfreunden bei cppreference und the C ++ - FAQ .
Codeliste mit den oben genannten Techniken .
Als Randnotiz zum fehlerhaften Codebeispiel; g ++ warnt davor wie folgt
quelle
Wenn Ihre Include-Pfade unterschiedlich sind
Linker-Fehler können auftreten, wenn eine Header-Datei und die zugehörige gemeinsam genutzte Bibliothek (.lib-Datei) nicht mehr synchron sind. Lassen Sie mich erklären.
Wie funktionieren Linker? Der Linker vergleicht eine Funktionsdeklaration (im Header deklariert) mit ihrer Definition (in der gemeinsam genutzten Bibliothek), indem er ihre Signaturen vergleicht. Sie können einen Linkerfehler erhalten, wenn der Linker keine Funktionsdefinition findet, die perfekt übereinstimmt.
Ist es möglich, immer noch einen Linkerfehler zu erhalten, obwohl die Deklaration und die Definition übereinstimmen? Ja! Sie sehen im Quellcode vielleicht gleich aus, aber es hängt wirklich davon ab, was der Compiler sieht. Im Wesentlichen könnten Sie mit einer Situation wie dieser enden:
Beachten Sie, dass beide Funktionsdeklarationen im Quellcode zwar identisch aussehen, sich jedoch je nach Compiler stark unterscheiden.
Sie könnten fragen, wie man in einer solchen Situation landet? Natürlich auch Pfade einschließen ! Wenn beim Kompilieren der gemeinsam genutzten Bibliothek der Include-Pfad zu führt
header1.h
und Sie ihn letztendlichheader2.h
in Ihrem eigenen Programm verwenden, kratzen Sie sich am Header und fragen sich, was passiert ist (Wortspiel beabsichtigt).Ein Beispiel, wie dies in der realen Welt geschehen kann, wird unten erläutert.
Weitere Ausarbeitung mit einem Beispiel
Ich habe zwei Projekte:
graphics.lib
undmain.exe
. Beide Projekte hängen ab voncommon_math.h
. Angenommen, die Bibliothek exportiert die folgende Funktion:Und dann nehmen Sie die Bibliothek in Ihr eigenes Projekt auf.
Boom! Sie erhalten einen Linker-Fehler und wissen nicht, warum er fehlschlägt. Der Grund dafür ist, dass die gemeinsame Bibliothek unterschiedliche Versionen desselben Includes verwendet
common_math.h
(ich habe dies hier im Beispiel durch Einfügen eines anderen Pfads deutlich gemacht, aber es ist möglicherweise nicht immer so offensichtlich. Möglicherweise unterscheidet sich der Include-Pfad in den Compilereinstellungen). .Beachten Sie in diesem Beispiel, dass der Linker Ihnen sagt, dass er nicht gefunden werden konnte
draw()
, wenn Sie in Wirklichkeit wissen, dass er offensichtlich von der Bibliothek exportiert wird. Sie könnten Stunden damit verbringen, sich am Kopf zu kratzen und sich zu fragen, was schief gelaufen ist. Die Sache ist, der Linker sieht eine andere Signatur, weil die Parametertypen leicht unterschiedlich sind. Im Beispielvec3
ist in beiden Projekten für den Compiler ein anderer Typ. Dies kann passieren, weil sie aus zwei leicht unterschiedlichen Include-Dateien stammen (möglicherweise stammen die Include-Dateien aus zwei verschiedenen Versionen der Bibliothek).Debuggen des Linkers
DUMPBIN ist Ihr Freund, wenn Sie Visual Studio verwenden. Ich bin sicher, dass andere Compiler ähnliche Tools haben.
Der Prozess läuft folgendermaßen ab:
[1] Mit Projekt meine ich eine Reihe von Quelldateien, die miteinander verknüpft sind, um entweder eine Bibliothek oder eine ausführbare Datei zu erstellen.
EDIT 1: Der erste Abschnitt wurde neu geschrieben, um das Verständnis zu erleichtern. Bitte kommentieren Sie unten, um mich wissen zu lassen, ob etwas anderes repariert werden muss. Vielen Dank!
quelle
Inkonsistente
UNICODE
DefinitionenEin Windows UNICODE-Build wird erstellt, wobei
TCHAR
usw. alswchar_t
usw. definiert wird . Wenn nicht mitUNICODE
definiert als Build mitTCHAR
definiert alschar
usw. erstellt wird. DieseUNICODE
und Definitionen_UNICODE
wirken sich auf alle "T
" Zeichenfolgentypen aus .LPTSTR
,LPCTSTR
Und ihr Elch.Das Erstellen einer Bibliothek mit
UNICODE
definierten und der Versuch, sie in einemUNICODE
nicht definierten Projekt zu verknüpfen, führt zu Linkerfehlern, da die Definition von nicht übereinstimmtTCHAR
.char
vs.wchar_t
.Der Fehler enthält normalerweise eine Funktion, einen Wert mit einem
char
oder einemwchar_t
abgeleiteten Typ, diese können auchstd::basic_string<>
usw. enthalten. Beim Durchsuchen der betroffenen Funktion im Code wird häufig aufTCHAR
oderstd::basic_string<TCHAR>
usw. verwiesen . Dies ist ein Hinweis darauf, dass der Code ursprünglich sowohl für einen UNICODE- als auch für einen Multi-Byte-Zeichen- (oder "schmalen") Build gedacht war .Um dies zu korrigieren, erstellen Sie alle erforderlichen Bibliotheken und Projekte mit einer konsistenten Definition von
UNICODE
(und_UNICODE
).Dies kann mit beiden erfolgen;
Oder in den Projekteinstellungen;
Oder in der Kommandozeile;
Die Alternative ist auch anwendbar, wenn UNICODE nicht verwendet werden soll, stellen Sie sicher, dass die Definitionen nicht festgelegt sind und / oder die Einstellung für mehrere Zeichen in den Projekten verwendet und konsistent angewendet wird.
Vergessen Sie nicht, auch zwischen den Builds "Release" und "Debug" konsistent zu sein.
quelle
Reinigen und wieder aufbauen
Eine "Bereinigung" des Builds kann das "tote Holz" entfernen, das möglicherweise von früheren Builds, fehlgeschlagenen Builds, unvollständigen Builds und anderen Build-System-bezogenen Build-Problemen übrig geblieben ist.
Im Allgemeinen enthält die IDE oder der Build eine Form der "sauberen" Funktion, die jedoch möglicherweise nicht korrekt konfiguriert ist (z. B. in einem manuellen Makefile) oder fehlschlägt (z. B. sind die Zwischen- oder resultierenden Binärdateien schreibgeschützt).
Überprüfen Sie nach Abschluss der "Bereinigung", ob die "Bereinigung" erfolgreich war und alle generierten Zwischendateien (z. B. ein automatisiertes Makefile) erfolgreich entfernt wurden.
Dieser Prozess kann als letzter Ausweg angesehen werden, ist aber oft ein guter erster Schritt . insbesondere, wenn der mit dem Fehler verbundene Code kürzlich hinzugefügt wurde (entweder lokal oder aus dem Quell-Repository).
quelle
Fehlendes "extern" in
const
Variablendeklarationen / -definitionen (nur C ++)Für Leute, die aus C kommen, könnte es eine Überraschung sein, dass globale
const
Variablen in C ++ eine interne (oder statische) Verknüpfung haben. In C war dies nicht der Fall, da alle globalen Variablen implizit sindextern
(dh wenn dasstatic
Schlüsselwort fehlt).Beispiel:
Richtig wäre es, eine Header-Datei zu verwenden und sie in file2.cpp und file1.cpp aufzunehmen
Alternativ könnte man die
const
Variable in file1.cpp mit explizit deklarierenextern
quelle
Obwohl dies eine ziemlich alte Frage mit mehreren akzeptierten Antworten ist, möchte ich Ihnen mitteilen, wie Sie einen obskuren "undefinierten Verweis auf" -Fehler beheben können .
Verschiedene Versionen von Bibliotheken
Ich habe einen Alias verwendet, um auf
std::filesystem::path
Folgendes zu verweisen : Das Dateisystem befindet sich seit C ++ 17 in der Standardbibliothek, aber mein Programm musste auch in C ++ 14 kompiliert werden. Daher habe ich beschlossen, einen variablen Alias zu verwenden:Angenommen, ich habe drei Dateien: main.cpp, file.h, file.cpp:
Beachten Sie die verschiedenen Bibliotheken, die in main.cpp und file.h verwendet werden. Da main.cpp # " file.h " nach < Dateisystem > enthält, war die dort verwendete Version des Dateisystems die C ++ 17- Version . Ich habe das Programm mit den folgenden Befehlen kompiliert:
$
g++ -g -std=c++17 -c main.cpp
-> kompiliert main.cpp zu main.o$
g++ -g -std=c++17 -c file.cpp
-> kompiliert file.cpp und file.h zu file.o$
g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> verknüpft main.o und file.oAuf diese Weise jede Funktion enthalten in file.o und in main.o verwendet , dass erforderlich
path_t
„undefined reference“ Fehler gab , weil main.o bezeichnetstd::filesystem::path
aber file.o zustd::experimental::filesystem::path
.Auflösung
Um dies zu beheben, musste ich nur <experimentelles :: Dateisystem> in file.h in <Dateisystem> ändern .
quelle
Stellen Sie beim Verknüpfen mit gemeinsam genutzten Bibliotheken sicher, dass die verwendeten Symbole nicht ausgeblendet sind.
Das Standardverhalten von gcc ist, dass alle Symbole sichtbar sind. Wenn die Übersetzungseinheiten jedoch mit einer Option erstellt werden
-fvisibility=hidden
, sind nur Funktionen, die mit gekennzeichnet__attribute__ ((visibility ("default")))
sind, im resultierenden gemeinsam genutzten Objekt extern.Sie können überprüfen, ob die gesuchten Symbole extern sind, indem Sie Folgendes aufrufen:
Die versteckten / lokalen Symbole werden
nm
mit einem Symboltyp in Kleinbuchstaben angezeigt , z. B.t
anstelle von `T für den Codeabschnitt:Sie können auch
nm
mit der Option verwenden-C
, um die Namen zu entwirren (wenn C ++ verwendet wurde).Ähnlich wie bei Windows-DLLs würde man öffentliche Funktionen mit einer Definition markieren, zum Beispiel
DLL_PUBLIC
definiert als:Was in etwa der Windows / MSVC-Version entspricht:
Weitere Informationen zur Sichtbarkeit finden Sie im gcc-Wiki.
Wenn eine Übersetzungseinheit mit
-fvisibility=hidden
den resultierenden Symbolen kompiliert wird, haben sie immer noch eine externe Verknüpfung (angezeigt mit dem Symboltyp Großbuchstaben vonnm
) und können problemlos für die externe Verknüpfung verwendet werden, wenn die Objektdateien Teil einer statischen Bibliothek werden. Die Verknüpfung wird nur dann lokal, wenn die Objektdateien mit einer gemeinsam genutzten Bibliothek verknüpft sind.So finden Sie heraus, welche Symbole in einer Objektdatei ausgeblendet sind:
quelle
nm -CD
oder verwendennm -gCD
, um externe Symbole anzuzeigen. Siehe auch Sichtbarkeit im GCC-Wiki.Unterschiedliche Architekturen
Möglicherweise sehen Sie eine Nachricht wie:
In diesem Fall bedeutet dies, dass die verfügbaren Symbole für eine andere Architektur gelten als die, für die Sie kompilieren.
In Visual Studio liegt dies an der falschen "Plattform", und Sie müssen entweder die richtige auswählen oder die richtige Version der Bibliothek installieren.
Unter Linux kann dies an dem falschen Bibliotheksordner liegen ( z. B.
lib
anstelle vonlib64
).Unter MacOS besteht die Möglichkeit, beide Architekturen in derselben Datei zu versenden. Es kann sein, dass der Link erwartet, dass beide Versionen vorhanden sind, aber nur eine. Es kann auch ein Problem mit dem falschen
lib
/lib64
Ordner sein, in dem die Bibliothek abgerufen wird.quelle