Qu'est-ce qu'une référence non définie/erreur de symbole externe non résolue et comment la corriger?
quelles sont les erreurs de symboles externes non définies ou non résolues? Quelles sont les causes communes et comment les corriger/les prévenir?
n'hésitez pas à éditer/ajouter le vôtre.
29 réponses
compiler un programme C++ se fait en plusieurs étapes, comme spécifié par 2.2 (crédits à Keith Thompson pour la référence) :
la priorité entre les règles de syntaxe de la traduction est spécifiée par les phases suivantes [Voir note de bas de page] .
- les caractères physiques du fichier source sont mappés, dans une implémentation-défini mode, à l'ensemble de caractères source de base (introduction de caractères nouveaux pour les indicateurs de fin de ligne) si nécessaire. [SNIP]
- chaque instance d'un caractère antislash (\) immédiatement suivi d'un caractère new-line est supprimé, en épissant les lignes physiques source à formez des lignes de source logiques. [SNIP]
- le fichier source est décomposé en jetons de prétraitement (2.5) et séquences de caractères blancs (y compris les commentaires). [SNIP]
Les directives de prétraitement- sont exécutées, les invocations macro sont étendues et les expressions opérateur unaire _Pragma sont exécutées. [SNIP]
- chaque membre du jeu de caractères source dans un caractère littéral ou une chaîne littérale, ainsi que chaque séquence d'échappement et universal-character-name un caractère littéral ou non raw littéral de chaîne est converti à le membre correspondant du jeu de caractères d'exécution; [SNIP]
- chaîne adjacente jetons littéraux sont concaténés.
- les caractères blancs séparant les jetons ne sont plus significatifs. Chaque jeton de prétraitement est converti en jeton. (2.7). Le résultant des jetons sont syntaxiquement et sémantiquement analysé et traduit en unité de traduction. [SNIP]
- Traduit en unités de traduction et d'instanciation unités sont combinées comme suit: [SNIP]
- tous les renvois à des entités externes sont résolus. Les composants de la bibliothèque sont liés pour satisfaire des références externes à des entités non définies dans le traduction en cours. Toutes ces sorties de traducteur sont rassemblées dans un image de programme qui contient les informations nécessaires à l'exécution dans son environnement d'exécution. (c'est moi qui souligne)
[note de bas de page] les mises en œuvre doivent se comporter comme si ces phases distinctes se produisaient, même si, dans la pratique, différentes phases peuvent être regroupées.
les erreurs spécifiées se produisent au cours de cette dernière étape de compilation, plus communément appelée lien. Cela signifie essentiellement que vous avez compilé un tas de fichiers d'implémentation dans des fichiers d'objet ou des bibliothèques et maintenant vous voulez obtenir à travailler ensemble.
dites que vous avez défini le symbole a
dans a.cpp
. Maintenant, b.cpp
a déclaré ce symbole et l'a utilisé. Avant de lier , il suppose simplement que ce symbole a été défini quelque part , mais il ne se soucie pas encore où. La phase de liaison est chargée de trouver le symbole et de le relier correctement à b.cpp
(en fait à l'objet ou à la bibliothèque qui l'utilise).
si vous utilisez Microsoft Visual Studio, vous verrez que les projets génèrent des fichiers .lib
. Ceux-ci contiennent une table de symboles exportés, et une table de symboles importés. Les symboles importés sont résolus par rapport aux bibliothèques contre lesquelles vous liez, et les symboles exportés sont fournis pour les bibliothèques qui utilisent ce .lib
(s'il y en a).
des mécanismes Similaires existent pour d'autres compilateurs/ plates-formes.
les messages D'erreur communs sont error LNK2001
, error LNK1120
, error LNK2019
pour Microsoft Visual Studio et undefined reference to
symbolName pour GCC .
le code:
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;
}
générera les erreurs suivantes avec 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
et erreurs similaires avec 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
causes courantes comprennent:
- défaut d'établir un lien avec les bibliothèques/fichiers d'objets appropriés ou de compiler des fichiers de mise en œuvre
- variable ou fonction déclarée et non définie.
- questions Communes avec la classe-les membres de type
- Modèle implémentations ne sont pas visibles. Les symboles
- ont été définis dans un programme C et utilisés dans le code C++.
- Incorrecte de l'importation/exportation de méthodes/classes entre les modules/dll. (MSVS specific)
- Circulaire de la bibliothèque de la dépendance
- référence non définie à "WinMain@16
- Interdépendants de l'ordre de la bibliothèque
- fichiers sources multiples du même nom
- erreur de frappe ou non, y compris les .lib extension lors de l'utilisation du
#pragma
(Microsoft Visual Studio) - Problèmes avec le modèle des amis
- incohérent
UNICODE
definitions
les membres de la Classe:
pur virtual
destructeur des besoins de mise en œuvre.
déclarer un destructeur pur exige encore que vous le définissiez (contrairement à une fonction régulière):
struct X
{
virtual ~X() = 0;
};
struct Y : X
{
~Y() {}
};
int main()
{
Y y;
}
//X::~X(){} //uncomment this line for successful definition
cela se produit parce que les destructeurs de classe de base sont appelés quand l'objet est détruit implicitement, donc une définition est nécessaire.
virtual
les méthodes doivent être mises en œuvre ou définies en tant que pur.
il s'agit d'une méthode semblable aux méthodes non virtual
sans définition, avec le raisonnement ajouté que
la déclaration pure génère un vtable factice et vous pourriez obtenir l'erreur de linker sans utiliser la fonction:
struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
int main()
{
Y y; //linker error although there was no call to X::foo
}
pour que cela fonctionne, déclarez X::foo()
comme pur:
struct X
{
virtual void foo() = 0;
};
Non virtual
membres du groupe
certains membres doivent être définis même s'ils ne sont pas utilisés explicitement:
struct A
{
~A();
};
l'erreur serait la suivante:
A a; //destructor undefined
la mise en œuvre peut être en ligne, dans la définition de classe elle-même:
struct A
{
~A() {}
};
ou à l'extérieur:
A::~A() {}
si l'implémentation est en dehors de la définition de classe, mais dans un en-tête, les méthodes doivent être marquées comme inline
pour éviter une définition multiple.
tous utilisés membre des méthodes doivent être définies si utilisé.
une erreur courante est d'oublier de qualifier le nom:
struct A
{
void foo();
};
void foo() {}
int main()
{
A a;
a.foo();
}
la définition devrait être
void A::foo() {}
static
les éléments de données doivent être définis à l'extérieur de la classe dans une Unité de traduction simple :
struct X
{
static int x;
};
int main()
{
int x = X::x;
}
//int X::x; //uncomment this line to define X::x
un initialiseur peut être fourni pour un static
const
membre de données d'integral ou type d'énumération dans la définition de classe; cependant, odr-utilisation de ce membre exigera toujours une définition de la portée de l'Espace-nom tel que décrit ci-dessus. C++11 permet l'initialisation à l'intérieur de la classe pour tous les membres de données static const
.
défaut d'établir un lien avec les bibliothèques/fichiers d'objets appropriés ou de compiler des fichiers de mise en œuvre
généralement, chaque unité de traduction génère un fichier objet qui contient les définitions des symboles définis dans cette unité de traduction. Pour utiliser ces symboles, vous devez établir un lien avec ces fichiers d'objets.
sous gcc vous spécifiez tous les fichiers d'objets qui doivent être reliés ensemble dans la ligne de commande, ou compiler les fichiers d'implémentation ensemble.
g++ -o test objectFile1.o objectFile2.o -lLibraryName
Le libraryName
ici est juste le strict nom de la bibliothèque, sans une plate-forme spécifique ajouts. Ainsi, par exemple sur les fichiers de bibliothèque Linux sont généralement appelés libfoo.so
, mais vous n'écririez que -lfoo
. Sous Windows , ce même fichier peut s'appeler foo.lib
, mais vous utilisez le même argument. Vous pourriez avoir à ajouter le répertoire où ces fichiers peuvent être trouvés en utilisant -L‹directory›
. Assurez-vous de ne pas écrire un espace après -l
ou -L
.
pour XCode : ajouter L'en-tête de L'utilisateur chemins de Recherche - > Ajouter le chemin de recherche de la Bibliothèque - > faire glisser et déposer la référence de la bibliothèque réelle dans le dossier du projet.
sous MSVS , les fichiers ajoutés à un projet sont automatiquement reliés entre eux et un fichier lib
est généré (dans l'usage courant). Pour utiliser les symboles dans un projet séparé, vous feriez
besoin d'inclure les fichiers lib
dans les paramètres du projet. Ceci est fait dans la section Linker des propriétés du projet, dans Input -> Additional Dependencies
. (le chemin vers le fichier lib
doit être
ajouté dans Linker -> General -> Additional Library Directories
) lorsque vous utilisez une bibliothèque tierce qui est fournie avec un fichier lib
, le défaut de le faire entraîne habituellement l'erreur.
il peut aussi arriver que vous oubliiez d'ajouter le fichier à la compilation, auquel cas le fichier objet ne sera pas généré. Dans gcc vous ajouteriez les fichiers à la ligne de commande. Dans MSVS ajouter le fichier au projet le fera compiler automatiquement (bien que les fichiers peuvent, manuellement, être exclus individuellement de la compilation).
dans la programmation de Windows, le signal que vous n'avez pas relié une bibliothèque nécessaire est que le nom du symbole non résolu commence par __imp_
. Recherchez le nom de la fonction dans la documentation, et il devrait dire quelle bibliothèque vous devez utiliser. Par exemple, MSDN place l'information dans une boîte au bas de chaque fonction dans une section appelée "BIBLIOTHÈQUE".
déclaré Mais n'a pas défini une variable ou une fonction.
une déclaration typique de variable est
extern int x;
comme il ne s'agit que d'une déclaration, Une définition unique est nécessaire. Une définition correspondante serait:
int x;
par exemple, ce qui suit générerait une erreur:
extern int x;
int main()
{
x = 0;
}
//int x; // uncomment this line for successful definition
des remarques similaires s'appliquent aux fonctions. Déclaration d'une fonction sans la définir conduit à l'erreur:
void foo(); // declaration only
int main()
{
foo();
}
//void foo() {} //uncomment this line for successful definition
veillez à ce que la fonction que vous implémentez corresponde exactement à celle que vous avez déclarée. Par exemple, vous avez peut-être mal apparié les qualificatifs cv:
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)
autres exemples de non-concordance:
- fonction/variable déclarée dans un espace de noms, défini dans un autre.
- fonction / variable déclarée comme membre de classe, définie comme globale (ou vice inversement.)
- Type de retour de fonction, nombre de paramètres et types, et convention d'appel ne sont pas tous exactement d'accord.
le message d'erreur du compilateur vous donnera souvent la déclaration complète de la variable ou de la fonction qui a été déclarée mais jamais définie. Comparer étroitement à la définition que vous avez fournis. assurez-vous que chaque détail correspond.
l'ordre dans lequel les bibliothèques interdépendantes liées sont spécifiées est erroné.
l'ordre dans lequel les bibliothèques sont liées est important si les bibliothèques dépendent les unes des autres. En général , si la bibliothèque A
dépend de la bibliothèque B
, alors libA
doit apparaître avant libB
dans les drapeaux de linker.
par exemple:
// 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;
};
créer les bibliothèques:
$ 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
compiler:
$ 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
donc pour répéter encore, l'ordre fait question!
qu'est ce qu'un "undefined reference/symbole externe non résolu"
je vais essayer d'expliquer ce qui est une"référence non définie/symbole externe non résolu".
note: j'utilise g++ et Linux et tous les exemples sont pour cela
par exemple, nous avons un 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;
}
et
// 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);
}
Faire l'objet d'fichiers
$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o
après la phase assembleur, nous avons un fichier objet, qui contient tous les symboles à exporter. Regardez les symboles
$ 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]
j'ai rejeté certaines lignes de la sortie, parce qu'elles n'ont pas d'importance
ainsi, nous voyons suivre les symboles pour exporter.
[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable
src2.cpp n'exporte rien et nous n'avons vu aucun de ses symboles
le Lien entre nos fichiers de l'objet
$ g++ src1.o src2.o -o prog
et l'exécuter
$ ./prog
123
Linker voit les symboles exportés et les liens. Maintenant, nous essayons de découpler les lignes dans src2.rpc comme ici
// 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);
}
et reconstruire un fichier objet
$ g++ -c src2.cpp -o src2.o
OK (pas d'erreurs), parce que nous construisons seulement le fichier objet, le lien n'est pas encore fait. Essayez de lier
$ 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
C'est arrivé parce que notre nom_var_local est statique, c'est-à-dire qu'il n'est pas visible pour les autres modules. Maintenant, plus profondément. Obtenir la sortie de la phase de traduction
$ 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
donc, nous avons vu qu'il n'y a pas d'étiquette pour local_var_name, c'est pourquoi linker ne l'a pas trouvé. Mais nous sommes des pirates :) et peuvent résoudre le problème. Ouvrez src1.s dans votre éditeur de texte et modifier
.local _ZL14local_var_name
.comm _ZL14local_var_name,4,4
à
.globl local_var_name
.data
.align 4
.type local_var_name, @object
.size local_var_name, 4
local_var_name:
.long 456789
c'est à dire, vous devriez avoir comme ci-dessous
.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:
; ...
nous avons changé la visibilité de local_var_name et définit sa valeur à 456789. Essayez de créer un fichier objet
$ g++ -c src1.s -o src2.o
ok, voir sortie readelf (symboles)
$ readelf --symbols src1.o
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
maintenant local_var_name a Bind GLOBAL (was LOCAL)
lien
$ g++ src1.o src2.o -o prog
et l'exécuter
$ ./prog
123456789
ok, nous hack :)
Donc, comme un résultat - un "undefined reference/externe non résolu erreur de symbole " se produit lorsque le linker ne peut pas trouver de symboles globaux dans les fichiers d'objet.
ont été définis dans un programme C et utilisés dans le code C++.
la fonction (ou variable) void foo()
a été définie dans un programme C et vous tentez de l'utiliser dans un programme C++:
void foo();
int main()
{
foo();
}
le linker C++ s'attend à ce que les noms soient altérés, donc vous devez déclarer la fonction comme:
extern "C" void foo();
int main()
{
foo();
}
de manière équivalente, au lieu d'être défini dans un programme C, la fonction (ou variable) void foo()
a été définie en C++ mais avec liaison C:
extern "C" void foo();
et vous tentez de l'utiliser dans un programme C++ avec liaison C++.
si une bibliothèque entière est incluse dans un fichier d'en-tête (et a été compilée sous forme de code C); l'inclusion devra se faire comme suit:
extern "C" {
#include "cheader.h"
}
si tout le reste échoue, recompilez.
j'ai récemment pu me débarrasser d'une erreur externe non résolue dans Visual Studio 2012 en recompilant le fichier incriminé. Quand j'ai re-construit, l'erreur a disparu.
cela se produit généralement lorsque deux (ou plus) bibliothèques ont une dépendance cyclique. La bibliothèque a tente d'utiliser les symboles de B. lib et la bibliothèque B tente d'utiliser les symboles de A. lib. Ni exister pour commencer. Lorsque vous essayer de compiler A, l'étape de lien échouera parce qu'il ne peut pas trouver B. lib. A. lib sera généré, mais pas de dll. Vous compilez ensuite B, qui réussira et générera B. lib. La recompilation de A va maintenant marcher parce que B. lib est maintenant trouvé.
Incorrecte de l'importation/exportation de méthodes/classes entre les modules/dll (compilateur spécifique).
MSVS exige que vous spécifiez quels symboles exporter et importer en utilisant __declspec(dllexport)
et __declspec(dllimport)
.
cette double fonctionnalité est généralement obtenue par l'utilisation d'une macro:
#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif
la macro THIS_MODULE
ne serait définie que dans le module qui exporte la fonction. De cette façon, la déclaration:
DLLIMPEXP void foo();
se développe en
__declspec(dllexport) void foo();
et dit au compilateur d'exporter la fonction, car le module actuel contient sa définition. Lorsque la déclaration serait insérée dans un autre module, elle serait portée à
__declspec(dllimport) void foo();
et indique au compilateur que la définition se trouve dans l'une des bibliothèques contre lesquelles vous avez établi un lien (Voir aussi 1) ).
vous pouvez comparer classes d'importation / d'exportation:
class DLLIMPEXP X
{
};
les implémentations du modèle ne sont pas visibles.
Les modèles non spécialisés doivent avoir leurs définitions visibles à toutes les unités de traduction qui les utilisent. Cela signifie que vous ne pouvez pas séparer la définition d'un modèle
pour un fichier d'implémentation. Si vous devez séparer l'implémentation, la solution habituelle est d'avoir un fichier impl
que vous incluez à la fin de l'en-tête qui
déclare le modèle. Une situation courante est:
template<class T>
struct X
{
void foo();
};
int main()
{
X<int> x;
x.foo();
}
//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}
à corriger cela, vous devez déplacer la définition de X::foo
dans le fichier d'en-tête ou un endroit visible à l'Unité de traduction qui l'utilise.
les gabarits spécialisés peuvent être implémentés dans un fichier d'implémentation et l'implémentation ne doit pas être visible, mais la spécialisation doit être préalablement déclarée.
pour plus d'explications et une autre solution possible (instanciation explicite) voir cette question et réponse .
C'est l'un des messages d'erreur les plus confus que tous les programmeurs VC++ ont vu à plusieurs reprises. Nous allons faire les choses à la clarté de la première.
A. Qu'est-ce que le symbole? En bref, un symbole est un nom. Il peut s'agir d'un nom de variable, d'un nom de fonction, d'un nom de classe, d'un nom de typedef, ou de n'importe quoi sauf ces noms et signes qui appartiennent au langage C++. Elle est définie par l'utilisateur ou introduite par une bibliothèque de dépendances (une autre est définie par l'utilisateur).
B. Qu'est-ce qui est externe?
En VC++, chaque fichier source (.rpc.c, etc. est considéré comme une unité de traduction, le compilateur compile une unité à la fois, et de générer un fichier objet(.obj) pour l'Unité de traduction actuelle. (Notez que chaque fichier d'en-tête inclus dans ce fichier source sera pré-traité et considéré comme faisant partie de cette unité de traduction)Tout ce qui se trouve dans une unité de traduction est considéré comme interne, tout le reste est considéré comme externe. Dans C++, vous pouvez faire référence à un symbole externe en utilisant des mots clés comme extern
, __declspec (dllimport)
et ainsi de suite.
C. qu'est-ce que"résoudre"? Résoudre est un liant à temps. Dans le temps de liaison, linker tente de trouver la définition externe de chaque symbole dans les fichiers objets qui ne peuvent pas trouver sa définition en interne. La portée de ce processus de recherche, y compris:
- tous les fichiers d'objets générés lors de la compilation du temps
- toutes les bibliothèques (.lib) qui sont explicites ou implicites spécifié comme dépendances supplémentaires de cette application de bâtiment.
ce processus de recherche s'appelle resolve.
D. enfin, pourquoi un symbole externe non résolu? Si le linker ne trouve pas la définition externe d'un symbole qui n'a pas de définition interne, il signale une erreur de symbole externe non résolue.
E. causes possibles de LNK2019 : erreur de symbole externe non résolue. Nous savons déjà que cette erreur est due au linker n'a pas trouvé la définition des symboles externes, les causes possibles peuvent être triées comme:
- définition existante
par exemple, si nous avons une fonction appelée foo définie dans a.cpp:
int foo()
{
return 0;
}
In b.cpp nous voulons appeler fonction foo, donc nous ajoutons
void foo();
pour déclarer la fonction foo (), et l'appeler dans un autre corps de fonction, dire bar()
:
void bar()
{
foo();
}
Maintenant, lorsque vous construisez ce code, vous obtiendrez une erreur LNK2019 qui se plaindra que foo est un symbole non résolu. Dans ce cas, nous savons que les foo() a sa définition dans a.cpp mais différent de celui que nous appelons(différentes valeur de retour). C'est le cas de cette définition existe.
- La définition n'existe pas
si nous voulons appeler certaines fonctions dans une bibliothèque, mais la bibliothèque d'importation n'est pas ajoutée dans la liste des dépendances supplémentaires (définie à partir de: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
) de votre paramètre de projet. Maintenant, le linker signalera un LNK2019 puisque la définition n'existe pas dans la portée de recherche courante.
référence non définie à WinMain@16
ou similaire "inhabituel main()
référence du point d'entrée (spécialement pour visual-studio ).
vous avez peut-être oublié de choisir le bon type de projet avec votre IDE actuel. L'IDE peut vouloir lier par exemple les projets D'Application Windows à une telle fonction de point d'entrée (comme spécifié dans la référence manquante ci-dessus), au lieu de la fonction couramment utilisée int main(int argc, char** argv);
signature.
si votre IDE prend en charge Plain Console Projects vous pouvez choisir ce type de projet, au lieu d'un projet d'application windows.
Voici décision1 et case2 traitées plus en détail à partir d'un monde réel problème.
aussi si vous utilisez des bibliothèques tierces, assurez-vous d'avoir les bons binaires 32/64 bits
Microsoft offre un #pragma
pour référencer la bibliothèque correcte au moment du lien;
#pragma comment(lib, "libname.lib")
en plus du chemin de la bibliothèque incluant le répertoire de la bibliothèque, ceci devrait être le nom complet de la bibliothèque.
Visual Studio NuGet package doit être mis à jour pour les nouveaux outils de la version
je viens d'avoir ce problème en essayant de lier libpng avec Visual Studio 2013. Le problème est que le paquet n'avait que des bibliothèques pour Visual Studio 2010 et 2012.
la bonne solution est d'espérer que le développeur publie un paquet mis à jour, puis upgrade, mais cela a fonctionné pour moi en hacking dans un paramètre supplémentaire pour VS2013, pointant vers le VS2012 fichiers de la bibliothèque.
j'ai édité le paquet (dans le dossier packages
à l'intérieur du répertoire de la solution) en trouvant packagename\build\native\packagename.targets
et à l'intérieur de ce fichier, en copiant toutes les sections v110
. J'ai changé le v110
en v120
dans les champs de condition seulement étant très prudent de laisser les chemins de nom de fichier tout comme v110
. Cela a simplement permis à Visual Studio 2013 de se connecter aux bibliothèques pour 2012, et dans ce cas, cela a fonctionné.
supposons que vous ayez un grand projet écrit en c++ qui en a un millier .fichiers cpp et un milliers de .h fichiers.Et disons que le projet dépend aussi de dix bibliothèques statiques. Disons que nous sommes sur Windows et que nous construisons notre projet dans Visual Studio 20xx. Lorsque vous appuyez sur Ctrl + F7 Visual Studio pour commencer à compiler l'ensemble de la solution (supposons que nous n'avons qu'un seul projet dans la solution)
que signifie compilation ?
- recherche de Visual Studio dans le fichier .vcxproj et commencer à compiler chaque fichier qui a l'extension .rpc. L'ordre de compilation n'est pas défini.Vous ne devez donc pas supposer que le fichier principal.cpp est compilé en premier
- si .fichiers cpp dépend supplémentaires .h fichiers pour trouver des symboles qui peut ou ne peut pas être défini dans le fichier .cpp
- S'il en existe un .rpc fichier dans lequel le compilateur n'a pas pu trouver un symbole, un erreur de temps de compilateur soulève le message le symbole x n'a pas pu être trouvé
- pour chaque fichier avec extension .le rpc est généré un fichier objet .O et aussi Visual Studio écrit la sortie dans un fichier nommé ProjectName.Rpc.Propre.txt qui contient tous les fichiers d'objets qui doivent être traités par le linker.
la deuxième étape de la compilation est faite par Linker.L'éditeur de liens doit fusionner tout le fichier objet et construire finalement la sortie (qui peut être un exécutable ou une bibliothèque)
étapes dans la liaison d'un projet
- analyse tous les fichiers d'objets et trouve la définition qui a été seulement déclarée dans les en-têtes (par exemple: le code d'une méthode d'une classe comme est mentionné dans les réponses précédentes, ou l'événement l'initialisation d'une variable statique qui est membre à l'intérieur d'une classe)
- si un symbole n'a pas pu être trouvé dans les dossiers d'objet il est également recherché dans les bibliothèques supplémentaires.Pour ajouter une nouvelle bibliothèque à un projet propriétés de Configuration -> répertoires VC++ -> répertoires de bibliothèque et ici vous avez spécifié un dossier supplémentaire pour la recherche des bibliothèques et propriétés de Configuration -> Linker -> entrée pour spécifier le nom de la bibliothèque.
-Si le Linker ne pouvait pas trouver le symbole que vous écrivez dans un .rpc, il soulève une linker erreur qui peut sembler
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
Observation
- une fois que le Linker trouve un symbole, il ne le cherche pas dans d'autres bibliothèques
- L'ordre de liaison des bibliothèques n'importe .
- si Linker trouve un symbole externe dans une bibliothèque statique, il comprend le symbole de la sortie du projet.Cependant, si la Bibliothèque est partagée (dynamique ) il n'inclut pas le code (symboles ) dans la sortie, mais Run-Time crashes may occur
- assurez-vous d'écrire votre projet c++ syntaxe correcte.
- définissez tout votre symbole que vous déclarez dans vos fichiers d'en-tête
- utilisez
#pragma once
pour permettre au compilateur de ne pas inclure un en-tête s'il était déjà inclus dans le courant .cpp qui sont compilés - assurez-vous que votre bibliothèque externe ne contient pas de symboles qui pourraient entrer en conflit avec d'autres symboles que vous avez définis dans votre fichiers d'en-tête
- lorsque vous utilisez le modèle pour vous assurer que vous incluez la définition de chaque fonction de modèle dans le fichier d'en-tête pour permettre au compilateur de générer le code approprié pour n'importe quelles instanciations.
comment résoudre ce genre d'erreur
Erreur De Temps Du Compilateur:
Erreur De Temps De Linker
un bug dans le compilateur/IDE
j'ai eu récemment ce problème, et il s'est avéré c'était un bug Dans Visual Studio Express 2013 . J'ai dû supprimer un fichier source du projet et le ré-Ajouter pour surmonter le bogue.
étapes à suivre si vous croyez qu'il pourrait s'agir d'un bogue dans compilateur/IDE:
- nettoyer le projet (certains IDEs ont une option pour le faire, vous pouvez également faire manuellement en supprimant les les fichiers objets)
- essayer de lancer un nouveau projet, copier tout le code source de l'original.
Linked .le fichier lib est associé à A.dll
j'ai eu le même problème. Disons que j'ai des projets et des projets de test. J'avais effectivement relié le fichier lib pour MyProject au projet Testpro. Cependant, ce fichier lib a été produit comme DLL pour le MyProject a été construit. En outre, je n'ai pas contenu de code source pour toutes les méthodes dans le MyProject, mais seulement l'accès aux points D'entrée de la DLL.
Pour résoudre le problème, j'ai construit le MyProject comme une LIB, et lié projet d'essai .fichier lib (j'ai copier coller le générés .fichier lib dans le dossier TestProject). Je peux alors construire à nouveau MyProject comme DLL. Il est compilé car la lib à laquelle TestProject est lié contient du code pour toutes les méthodes dans les classes de MyProject.
utiliser le linker pour aider à diagnostiquer l'erreur
"151970920 Plus" moderne linkers inclure une option verbose qui imprime à des degrés divers;- Lien invocation (ligne de commande),
- des Données sur ce que les bibliothèques sont inclus dans le lien de la scène,
- L'emplacement des bibliothèques,
- les chemins de Recherche utilisé.
pour gcc et clang; vous voulez ajouter typiquement -v -Wl,--verbose
ou -v -Wl,-v
à la ligne de commande. Plus de détails peuvent être trouvés ici,
- Linux ld page de manuel de .
- LLVM de l'éditeur de liens de la page .
- "Une introduction à la GCC" chapitre 9 .
pour MSVC, /VERBOSE
(en particulier /VERBOSE:LIB
) est ajouté à la ligne de commande link.
- La page MSDN sur le
/VERBOSE
linker option .
puisque les gens semblent être dirigés à cette question quand il s'agit d'erreurs de linker, je vais ajouter ceci ici.
une des raisons possibles des erreurs de linker avec GCC 5.2.0 est qu'une nouvelle bibliothèque libstdc++ ABI est maintenant choisie par défaut.
si vous obtenez des erreurs de linker sur des références non définies à des symboles qui impliquent des types dans l'espace de nom std::__cxx11 ou la balise [abi:cxx11] alors il indique probablement que vous essayez de lier ensemble des fichiers d'objets qui ont été compilés avec des valeurs différentes pour la macro _GLIBCXX_USE_CXX11_ABI. Cela se produit souvent lorsque le lien vers une bibliothèque tierce partie qui a été compilé avec une version plus ancienne de GCC. Si la bibliothèque tierce ne peut pas être reconstruite avec la nouvelle ABI, alors vous devrez recompiler votre code avec l'ancienne ABI.
donc si vous avez soudainement des erreurs de linker en passant à un GCC après 5.1.0 ce serait une chose à vérifier.
un wrapper autour de GNU ld qui ne supporte pas les scripts de linker
un peu .les fichiers sont donc en fait GNU ld linker scripts , par exemple libtbb.ainsi fichier est un fichier texte ASCII avec ce contenu:
INPUT (libtbb.so.2)
certaines constructions plus complexes peuvent ne pas supporter cela. Par exemple, si vous incluez -v dans les options du compilateur, vous pouvez voir que le mainwin gcc wrapper mwdip écarte les fichiers de commande de script de linker dans la liste de sortie verbeuse des bibliothèques à relier. Une solution simple consiste à remplacer le fichier de commande d'entrée du script de linker par une copie du fichier à la place (ou un lien symbolique), par exemple
cp libtbb.so.2 libtbb.so
ou vous pouvez remplacer l'argument-l par le chemin complet du .ainsi, par exemple au lieu de -ltbb
do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
se lier d'amitié avec des modèles...
étant donné l'extrait de code d'un type de modèle avec un opérateur ami (ou une fonction);
template <typename T>
class Foo {
friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};
le operator<<
est déclaré comme une fonction non-modèle. Pour chaque type T
utilisé avec Foo
, il doit y avoir un operator<<
Non-Templé . Par exemple, s'il y a un type Foo<int>
déclaré, Alors il doit y avoir une implémentation de l'opérateur comme suit:
std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
Puisqu'il n'est pas implémenté, le linker ne le trouve pas et entraîne l'erreur.
pour corriger ceci, vous pouvez déclarer un opérateur de modèle avant le type Foo
et ensuite déclarer comme un ami, l'instanciation appropriée. La syntaxe est un peu maladroite, mais elle se présente comme suit:
// 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
}
le code ci-dessus limite l'amitié de l'opérateur à l'instanciation correspondante de Foo
, i.e. l'instance operator<< <int>
est limitée à l'accès aux membres privés de l'instance Foo<int>
.
les variantes comprennent;
-
permettant à l'amitié d'étendre à toutes les instanciations des gabarits, comme suit;
template <typename T> class Foo { template <typename T1> friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a); // ... };
-
Ou, de la mise en œuvre de la
operator<<
qui peut être fait inline à l'intérieur de la définition de la classe;template <typename T> class Foo { friend std::ostream& operator<<(std::ostream& os, const Foo& a) { /*...*/ } // ... };
Note , lorsque la déclaration de l'opérateur (ou de la fonction) apparaît seulement dans la classe, le nom n'est pas disponible pour la recherche "normale", seulement pour la recherche dépendante de l'argument, de cppreference ;
un nom déclaré pour la première fois dans une déclaration d'ami dans le modèle de classe ou de classe X devient membre de L'espace de noms le plus proche de X, mais n'est pas accessible pour recherche (à l'exception de la recherche dépendante des arguments qui considère X) à moins qu'une déclaration de correspondance à l'étendue de l'espace de noms ne soit fournie...
il y a d'autres lectures sur les modèles d'amis à cppreference et le C++ FAQ .
liste de codes montrant les techniques au-dessus de .
comme une note de côté à la Echec de l'échantillon de code; G++ met en garde à ce sujet comme suit
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)
inconsistant UNICODE
definitions
Windows version UNICODE est construit avec une TCHAR
etc. étant défini comme wchar_t
etc. Si vous ne construisez pas avec UNICODE
défini comme construire avec TCHAR
défini comme char
etc. Ces UNICODE
et _UNICODE
définit affecter tous les T
" types de chaîne de caractères ; LPTSTR
, LPCTSTR
et leur élan.
construire une bibliothèque avec UNICODE
défini et tenter de le relier dans un projet où UNICODE
n'est pas défini entraînera des erreurs de linker car il y aura une inadéquation dans la définition de TCHAR
; char
vs. wchar_t
.
l'erreur inclut habituellement une fonction une valeur avec un char
ou wchar_t
type dérivé, ceux-ci pourraient inclure std::basic_string<>
etc. ainsi. En parcourant la fonction affectée dans le code, il y aura souvent une référence à TCHAR
ou std::basic_string<TCHAR>
etc. Il s'agit d'un signal indiquant que le code était à l'origine destiné à la fois à un caractère UNICODE et à un caractère Multi-octets (ou "étroit").
pour corriger cela, construire toutes les bibliothèques et les projets nécessaires avec une définition cohérente de UNICODE
(et _UNICODE
).
-
cela peut être fait avec l'un ou l'autre;
#define UNICODE #define _UNICODE
-
ou paramètres du projet;
Propriétés Du Projet > Général > Paramètres Par Défaut Projet > Jeu De Caractères
-
ou sur la ligne de commande;
/DUNICODE /D_UNICODE
l'alternative est également applicable, si UNICODE n'est pas destiné à être utilisé, assurez-vous que les definitions ne sont pas définies, et/ou le cadre à plusieurs caractères est utilisé dans les projets et appliqué de manière cohérente.
N'oubliez pas d'être cohérent entre la "Libération" et "Debug" construit ainsi.
nettoyer et reconstruire
un" nettoyage "de la construction peut enlever le" bois mort " qui peut être laissé traîner des constructions précédentes, des constructions défectueuses, des constructions incomplètes et d'autres problèmes de construction liés au système de construction.
en général, L'IDE ou la construction inclura une certaine forme de fonction" clean", mais celle-ci peut ne pas être correctement configurée (par exemple dans un makefile manuel) ou peut échouer (par exemple les binaires intermédiaires ou résultants sont en lecture seule).
une fois le "nettoyage" terminé, vérifiez que le "nettoyage" a réussi et que tout le fichier intermédiaire généré (par exemple un makefile automatisé) a été supprimé.
ce processus peut être considéré comme un recours final, mais est souvent une bonne première étape ; surtout si le code lié à l'erreur a été récemment ajouté (localement ou à partir du dépôt source).
votre linkage consomme des bibliothèques avant les fichiers d'objets qui s'y rapportent
- vous essayez de compiler et de lier votre programme avec la chaîne D'outils GCC.
- Votre lien spécifie que toutes les bibliothèques et la bibliothèque des chemins de recherche
- si
libfoo
dépend delibbar
, alors votre lien place correctementlibfoo
avantlibbar
. - votre lien échoue avec les erreurs
undefined reference to
something . - mais tous les indéfini quelque chose s sont déclarés dans les fichiers d'en-tête que vous avez
#include
d et sont en fait définies dans les bibliothèques que vous liez.
exemples sont en C. Ils pourraient aussi bien être C++
un exemple minimal impliquant une bibliothèque statique que vous avez construite vous-même
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;
}
vous construisez votre bibliothèque statique:
$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o
vous compilez votre programme:
$ gcc -I. -c -o eg1.o eg1.c
vous essayez de le lier avec libmy_lib.a
et échouez:
$ 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
Le même résultat si vous compilez et liez en une seule étape, comme:
$ 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
exemple minimal impliquant une bibliothèque de systèmes partagés, la bibliothèque de compression libz
eg2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%s\n",zlibVersion());
return 0;
}
compilez votre programme:
$ gcc -c -o eg2.o eg2.c
essayez de lier votre programme avec libz
et échouez:
$ 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
Same si vous compilez et liez en une seule fois:
$ 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
et une variante de l'exemple 2 concernant 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-ce que tu fais de mal?
dans la séquence de fichiers d'objets et de bibliothèques que vous souhaitez lier pour faire votre programme, vous placez les bibliothèques avant les fichiers d'objet qui se réfèrent ils. Vous devez placer les bibliothèques après les fichiers d'objet qui se réfèrent de ils.
Lien de l'exemple 1 correctement:
$ gcc -o eg1 eg1.o -L. -lmy_lib
Succès:
$ ./eg1
Hello World
"15191100920 Lien" exemple 2:
$ gcc -o eg2 eg2.o -lz
Succès:
$ ./eg2
1.2.8
relier l'exemple 2 pkg-config
variation correctement:
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
l'explication
La Lecture est facultative à partir de Maintenant .
par défaut, une commande de liaison générée par GCC, sur votre distro, consomme les fichiers dans le lien de gauche à droite dans ligne de commande de la séquence. Quand il trouve qu'un fichier se réfère à quelque chose et ne contient pas de définition pour elle, de chercher une définition dans les dossiers plus à droite. Si elle trouve finalement une définition, la le renvoi est résolu. Si des renvois demeurent non résolus à la fin, lien echec: le linker ne cherche pas à l'envers.
premier, exemple 1 , avec bibliothèque statique my_lib.a
une bibliothèque statique est une archive indexée de fichiers d'objets. Lorsque l'éditeur de liens
trouve -lmy_lib
dans la séquence de liaison et montre que cela se réfère
à la bibliothèque statique ./libmy_lib.a
, il veut savoir si votre programme
a besoin de l'un des fichiers d'objets dans libmy_lib.a
.
Il n'y a qu'un fichier objet dans libmy_lib.a
, à savoir my_lib.o
, et il n'y a qu'une chose définie
dans my_lib.o
, à savoir la fonction hw
.
le linker décidera que votre programme a besoin my_lib.o
si et seulement si elle sait déjà que
votre programme se réfère à hw
, dans un ou plusieurs des fichiers objets qu'il possède déjà
ajouté au programme, et qu'aucun des fichiers de l'objet qu'il a déjà ajouté
contient une définition pour hw
.
si c'est vrai, alors le linker extraira une copie de my_lib.o
de la bibliothèque et
ajouter à votre programme. Ensuite, votre programme contient une définition pour hw
, donc
ses références à hw
sont résolu .
quand vous essayez de lier le programme comme:
$ gcc -o eg1 -L. -lmy_lib eg1.o
l'éditeur de liens n'a pas encore ajouté de eg1.o
à la programme quand il voit
-lmy_lib
. Parce qu'à ce moment-là, il n'a pas vu eg1.o
.
Votre programme ne fait pas encore de référence à hw
: il
ne fait pas encore de références du tout , parce que toutes les références qu'il fait
sont dans eg1.o
.
de sorte que le linker n'ajoute pas my_lib.o
au programme et n'a pas d'autres
utiliser pour libmy_lib.a
.
ensuite, il trouve eg1.o
, et l'ajoute à être programme. Un fichier objet dans le
la séquence de couplage est toujours ajoutée au programme. Maintenant, le programme fait
une référence à hw
, et ne contient pas de définition de hw
; mais
il ne reste rien dans la séquence de liaison qui pourrait fournir les
définition. La référence à hw
se termine par non résolu , et la liaison échoue.
Second, exemple 2 , avec bibliothèque libz
une bibliothèque partagée n'est pas une archive de fichiers d'objets ou quelque chose comme ça. C'est
beaucoup plus comme un programme qui n'a pas une fonction main
et
expose plutôt plusieurs autres symboles qu'il définit, de sorte que d'autres
les programmes peuvent utiliser lors de l'exécution.
beaucoup de distros Linux aujourd'hui configurer leur chaîne D'outils GCC de sorte que ses pilotes de langue( gcc
, g++
, gfortran
etc)
donner instruction au système linker ( ld
) de relier les bibliothèques partagées sur une base selon les besoins .
Vous en avez un de ces distributions.
cela signifie que lorsque le linker trouve -lz
dans la séquence de liaison, et il ressort que cela se réfère
à la bibliothèque partagée (say) /usr/lib/x86_64-linux-gnu/libz.so
, il veut savoir si les références qu'il a ajouté à votre programme qui ne sont pas encore définis ont des définitions qui sont exportées par libz
si cela est vrai, alors le linker pas copier tous les morceaux de libz
et
ajoutez-les à votre programme; au lieu de cela, il ne fera que soigner le code de votre programme
de sorte que:-
-
à l'exécution, le chargeur de programme système chargera une copie de
libz
même processus que votre programme quand il charge une copie de votre programme pour l'exécuter. -
à l'exécution, chaque fois que votre programme se réfère à quelque chose qui est défini dans
libz
, qui référence utilise la définition exportées par la copie delibz
le même processus.
votre programme ne vise qu'une chose dont la définition est exportée par libz
,
à savoir la fonction zlibVersion
, à laquelle il n'est fait référence qu'une seule fois, dans eg2.c
.
Si le linker ajoute cette référence à votre programme, puis trouve la définition
exporté par libz
, la référence est résolu
mais quand vous essayez de lier le programme comme:
gcc -o eg2 -lz eg2.o
l'ordre des événements est erroné de la même manière qu'avec l'exemple 1.
Au moment où le linker trouve -lz
, il y a Non références à quoi que ce soit
au programme: ils sont tous dans eg2.o
, qui n'a pas encore été vu. De sorte que le
linker décide qu'il n'a pas d'utilité pour libz
. Quand il atteint eg2.o
, l'ajoute au programme,
et puis A Référence non définie à zlibVersion
, la séquence de liaison est terminée;
cette référence n'est pas résolue et le lien échoue.
enfin, la variation pkg-config
de l'exemple 2 a une explication maintenant évidente.
Après l'expansion de la coque:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
devient:
gcc -o eg2 -lz eg2.o
qui est juste l'exemple 2 Encore une fois.
je peux reproduire le problème dans l'exemple 1, mais pas dans l'exemple 2
La liaison:
gcc -o eg2 -lz eg2.o
fonctionne très bien pour vous!
(Ou: ce lien a bien fonctionné pour vous, dites, Fedora 23, mais échoue sur Ubuntu 16.04)
c'est parce que la distro sur laquelle la tringlerie fonctionne est l'une de celles qui ne pas configurer sa chaîne D'outils GCC pour relier les bibliothèques partagées as-needed .
à l'époque, il était normal que les systèmes de type unix relient statiques et partagés. bibliothèques selon des règles différentes. Des bibliothèques statiques dans une séquence de liaison ont été reliées sur la base selon les besoins expliquée dans l'exemple 1, mais les bibliothèques partagées étaient liées inconditionnellement.
ce comportement est économique à l'Heure de liaison parce que le ne doit pas réfléchir si une bibliothèque partagée est requis par le programme: si c'est une bibliothèque partagée, un lien. Et la plupart des bibliothèques dans la plupart des liens sont des bibliothèques partagées. Mais il y a aussi des inconvénients: -
-
Il n'est pas rentable à runtime , car il peut causer des bibliothèques partagées pour être chargé avec un programme, même si n'a pas besoin d'eux.
-
les différentes les règles de couplage pour les bibliothèques statiques et partagées peuvent prêter à confusion aux programmeurs inexpérimentés, qui ne savent peut-être pas si
-lfoo
dans leur liaison va se résoudre à/some/where/libfoo.a
ou à/some/where/libfoo.so
, et pourrait ne pas comprendre la différence entre partagés et les bibliothèques statiques de toute façon.
Ce compromis a conduit à la situation schismatique aujourd'hui. Certaines distributions ont modifié leurs règles de liaison GCC pour les bibliothèques partagées de sorte que le selon les besoins principe s'applique pour toutes les bibliothèques. Certaines distributions ont coincé avec le vieux façon.
Pourquoi j'obtiens toujours ce problème, même si je compile-et-link en même temps?
Si je viens de le faire:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
sûrement gcc doit compiler eg1.c
d'abord, et puis lier le résultat
fichier d'objets avec libmy_lib.a
. Alors comment peut-il ne pas connaître ce fichier objet
est nécessaire quand il fait la lier?
parce que compiler et lier avec une seule commande ne change pas la ordre de la séquence de liaison.
lorsque vous exécutez la commande ci-dessus, gcc
se rend compte que vous voulez compilation +
lien. Donc dans les coulisses, il génère une commande de compilation, et exécute
il, puis génère une commande de liaison, et l'exécute, comme si vous avait exécuté le
deux commandes:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
donc le linkage échoue tout comme si vous do exécutez ces deux commandes. Le
seule différence que l'on remarque dans l'échec est que gcc a généré un
fichier objet temporaire dans le cas de compilation + lien, parce que vous ne le dites pas
pour utiliser eg1.o
. Nous voyons:
/tmp/ccQk1tvs.o: In function `main'
au lieu de:
eg1.o: In function `main':
Voir aussi
L'ordre dans lequel interdépendants les bibliothèques liées sont spécifiées est erroné
mettre les bibliothèques interdépendantes dans le mauvais ordre n'est qu'une façon dans lequel vous pouvez obtenir des fichiers que besoin définitions des choses à venir plus tard dans le couplage que les fichiers que fournir les définitions. Mettre les bibliothèques avant le les fichiers objets qui parlent d'eux est une autre façon de faire la même erreur.
quand vos chemins d'inclusion sont différents
les erreurs de Linker peuvent se produire lorsqu'un fichier d'en-tête et sa bibliothèque partagée associée (.fichier lib) sortir de la synchronisation. Laissez-moi vous expliquer.
Comment fonctionnent les linkers? Le linker fait correspondre une déclaration de fonction (déclarée dans l'en-tête) avec sa définition (dans la bibliothèque partagée) en comparant leurs signatures. Vous pouvez obtenir une erreur de linker si le linker ne trouve pas une définition de fonction qui correspond parfaitement.
est-il possible d'obtenir encore une erreur de linker même si la déclaration et la définition semblent correspondre? Oui! Ils peuvent ressembler au même dans le code source, mais cela dépend vraiment de ce que le compilateur voit. Essentiellement, vous pourriez vous retrouver avec une situation comme celle-ci:
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
remarquez que même si les deux déclarations de fonction semblent identiques dans le code source, elles sont vraiment différentes selon le compilateur.
Vous pourriez vous demander comment on finit dans une telle situation? chemins d'inclusion bien sûr! Si lors de la compilation de la bibliothèque partagée, le chemin include mène à header1.h
et que vous utilisez header2.h
dans votre propre programme, vous serez laissé à gratter votre en-tête en vous demandant ce qui s'est passé (jeu de mots intentionnel).
Un exemple de comment cela peut se produire dans le monde réel est expliqué ci-dessous.
développement ultérieur avec un exemple
j'ai deux projets: graphics.lib
et main.exe
. Les deux projets dépendent de common_math.h
. Supposons que la bibliothèque exporte la fonction suivante:
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
et puis vous allez de l'avant et inclure la bibliothèque dans votre propre projet.
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
Boom! Vous obtenez une erreur de linker et vous n'avez aucune idée pourquoi il échoue. La raison en est que la bibliothèque commune utilise différentes versions de la même inclure common_math.h
(I ont rendu évident ici dans l'exemple en incluant un chemin différent, mais il pourrait ne pas toujours être si évident. Peut-être que le chemin est différent dans les paramètres du compilateur).
Note dans cet exemple, le linker vous dirait qu'il ne pouvait pas trouver draw()
, alors qu'en réalité vous savez qu'il est évidemment exporté par la bibliothèque. Tu pourrais passer des heures à te gratter la tête en te demandant ce qui s'est passé. Le truc c'est que le linker voit une signature différente parce que le paramètre les types sont légèrement différentes. Dans l'exemple, vec3
est un type différent dans les deux projets en ce qui concerne le compilateur. Cela pourrait se produire parce qu'ils proviennent de deux fichiers include légèrement différents (peut-être que les fichiers include proviennent de deux versions différentes de la bibliothèque).
Débogage de l'éditeur de liens
DUMPBIN est votre ami, si vous utilisez Visual Studio. Je suis sûr que d'autres compilateurs ont d'autres outils similaires.
Le processus qui va comme ceci:
- Notez le nom étrangement Déformé donné dans l'erreur de l'attache. (eg. dessiner@graphiques@XYZ).
- Dump les symboles exportés de la bibliothèque dans un fichier texte.
- rechercher le symbole d'intérêt exporté et noter que le nom tronqué est différent.
- faites attention à la raison pour laquelle les noms mutilés ont fini différents. Vous serez en mesure de voir que la les types de paramètres sont différents, même s'ils se ressemblent dans le code source.
- raison pour laquelle ils sont différents. Dans l'exemple donné ci-dessus, ils sont différents en raison de différents fichiers à inclure.
[1] par projet, j'entends un ensemble de fichiers sources qui sont reliés entre eux pour produire soit une bibliothèque, soit un exécutable.
EDIT 1: réécrit la première section pour être plus facile à comprendre. S'il vous plaît commentaire ci-dessous pour me savoir si quelque chose doit être corrigé. Merci!
manquant "extern" dans const
déclarations/définitions variables (C++ seulement)
pour les personnes venant de C il pourrait être une surprise que dans C++ global const
variables ont un lien interne (ou statique). En C, ce n'était pas le cas, car toutes les variables globales sont implicitement extern
(c'est-à-dire lorsque le mot-clé static
est manquant).
exemple:
// 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
}
correcte serait d'utiliser un fichier d'en-tête et l'inclure dans fichier2.cpp et fil1.cpp
extern const int test;
extern int test2;
on peut aussi déclarer la variable const
dans file1.cpp avec extern
même s'il s'agit d'une question assez ancienne avec plusieurs réponses acceptées, j'aimerais partager comment résoudre une obscure " référence non définie à " erreur.
différentes versions de bibliothèques
j'utilisais un alias pour me référer à std::filesystem::path
: le système de fichiers est dans la bibliothèque standard depuis c++17 Mais mon programme avait besoin de compiler aussi en C++14 donc j'ai décidé d'utiliser 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
disons que j'ai trois fichiers: main.rpc, fichier.h, fichier.cpp:
- "151990920 de fichier".h #includes < experimental:: filestystem > et contient le code ci-dessus
- "151990920 de fichier".rpc , la mise en œuvre de fichier.h, # includ's " file.h "
- principal.cpp #includes < fichier > et fichier.h "
Note la bibliothèques utilisé à la main.le rpc et le fichier.h. Depuis principal.cpp # include " file.h " après < filestystem >, la version du système de fichiers utilisé était le C++17 un . J'ai utilisé pour compiler le programme avec les commandes suivantes:
$ g++ -g -std=c++17 -c main.cpp
-> compile à la main.cpp à main.o
$ g++ -g -std=c++17 -c file.cpp
-> compile le fichier.le rpc et le fichier.h à de fichier.o
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> liens principaux.o et fichier.o
de Cette façon une fonction contenues dans le fichier.o et utilisé en main.o que exigeait path_t
donnait" des erreurs de référence non définies parce que principal.o visée std::filesystem::path
mais fichier.o à std::experimental::filesystem::path
.
résolution
pour corriger cela j'ai juste eu besoin de changer
lors de la création de liens avec des bibliothèques partagées, assurez-vous que les symboles utilisés ne sont pas cachés.
Le comportement par défaut de gcc est que tous les symboles sont visibles. Cependant, lorsque les unités de traduction sont construites avec l'option -fvisibility=hidden
, seules les fonctions/symboles marqués par __attribute__ ((visibility ("default")))
sont externes dans l'objet partagé qui en résulte.
vous pouvez vérifier si les symboles que vous recherchez sont externes en invoquant:
# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL
les symboles cachés / locaux sont représentés par nm
avec le type de symbole en minuscules, par exemple t
au lieu de "T pour le code-section:
nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL
vous pouvez aussi utiliser nm
avec l'option -C
pour démanger les noms (si C++ a été utilisé).
similaire à Windows-dlls, on marquerait les fonctions publiques avec un define, par exemple DLL_PUBLIC
défini comme:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
qui approximativement correspond à Windows' / MSVC-version:
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
Plus de l'information à propos de la visibilité peut être trouvé sur la gcc wiki.
Lorsqu'une unité de traduction est compilée avec -fvisibility=hidden
, les symboles résultants ont encore un lien externe (indiqué par nm
) et peuvent être utilisés pour un lien externe sans problème si les fichiers objets deviennent une partie d'une bibliothèque statique. Le lien devient locale uniquement lorsque l'objet les fichiers liés dans une bibliothèque partagée.
pour trouver quels symboles dans un fichier objet sont cachés, Lancez:
>>> objdump -t XXXX.o | grep hidden
0000000000000000 g F .text 000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g F .text 000000000000000b .hidden HIDDEN_SYMBOL2