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.

1231
demandé sur Baum mit Augen 2012-09-25 02:27:40

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

  1. 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]
  2. 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]
  3. 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]
  4. Les directives de prétraitement
  5. sont exécutées, les invocations macro sont étendues et les expressions opérateur unaire _Pragma sont exécutées. [SNIP]
  6. 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]
  7. chaîne adjacente jetons littéraux sont concaténés.
  8. 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]
  9. Traduit en unités de traduction et d'instanciation unités sont combinées comme suit: [SNIP]
  10. 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:

716
répondu Luchian Grigore 2017-11-18 03:51:35

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 .

154
répondu Luchian Grigore 2014-09-09 01:06:32

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".

100
répondu Luchian Grigore 2016-08-23 09:32:00

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.

93
répondu Luchian Grigore 2015-06-23 15:54:29

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!

75
répondu Svalorzen 2014-07-10 11:46:08

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.

65
répondu Kastaneda 2017-08-09 13:30:37
Les symboles

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"
}
62
répondu Luchian Grigore 2014-10-17 07:25:35

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é.

59
répondu sgryzko 2013-12-03 20:56:14

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
{
};
51
répondu Luchian Grigore 2016-02-18 06:26:07

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 .

49
répondu Luchian Grigore 2017-05-23 12:10:46

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:

  1. 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.

  1. 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.

48
répondu Nima Soroush 2017-11-08 09:05:14

référence non définie à WinMain@16 ou similaire "inhabituel main() référence du point d'entrée (spécialement pour ).

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.

34
répondu πάντα ῥεῖ 2018-07-12 17:39:25

aussi si vous utilisez des bibliothèques tierces, assurez-vous d'avoir les bons binaires 32/64 bits

32
répondu Dula 2014-06-18 17:06:24

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.

32
répondu Niall 2014-09-10 06:37:35

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é.

31
répondu Malvineous 2015-07-28 03:52:36

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

  1. une fois que le Linker trouve un symbole, il ne le cherche pas dans d'autres bibliothèques
  2. L'ordre de liaison des bibliothèques n'importe .
  3. 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
  4. comment résoudre ce genre d'erreur

    Erreur De Temps Du Compilateur:

    • assurez-vous d'écrire votre projet c++ syntaxe correcte.

    Erreur De Temps De Linker

    • 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.
30
répondu eFarzad 2017-11-08 10:15:57

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.
26
répondu developerbmw 2017-05-23 12:18:30

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.

23
répondu octoback 2014-04-04 15:02:33

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,

pour MSVC, /VERBOSE (en particulier /VERBOSE:LIB ) est ajouté à la ligne de commande link.

23
répondu Niall 2016-02-24 10:41:08

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.

18
répondu Plankalkül 2015-09-10 11:03:35

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

17
répondu JDiMatteo 2015-03-30 20:47:37

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)

16
répondu Niall 2016-06-10 21:29:24

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 ).

  1. cela peut être fait avec l'un ou l'autre;

    #define UNICODE
    #define _UNICODE
    
  2. ou paramètres du projet;

    Propriétés Du Projet > Général > Paramètres Par Défaut Projet > Jeu De Caractères

  3. 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.

13
répondu Niall 2017-05-12 06:06:08

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).

12
répondu Niall 2016-02-24 12:41:09

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 de libbar , alors votre lien place correctement libfoo avant libbar .
  • 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 de libz 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.

11
répondu Mike Kinghan 2017-05-23 12:34:53

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:

  1. Notez le nom étrangement Déformé donné dans l'erreur de l'attache. (eg. dessiner@graphiques@XYZ).
  2. Dump les symboles exportés de la bibliothèque dans un fichier texte.
  3. rechercher le symbole d'intérêt exporté et noter que le nom tronqué est différent.
  4. 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.
  5. 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!

11
répondu fafaro 2017-11-12 00:10:26

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

7
répondu Andreas H. 2017-08-03 08:01:49

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 dans le fichier.h à .

2
répondu Stypox 2018-08-27 18:12:32

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
1
répondu ead 2018-09-10 08:18:38