Co to jest undefined reference / unsolved external symbol error i jak go naprawić?

Co to są undefined reference / nierozwiązane zewnętrzne błędy symboliczne? Jakie są najczęstsze przyczyny i jak je naprawić / zapobiec?

Możesz edytować/dodawać własne.

Author: Baum mit Augen, 2012-09-25

29 answers

Kompilowanie programu C++ odbywa się w kilku krokach, określonych przez 2.2 (podziękowania dla Keitha Thompsona za odniesienie):

Pierwszeństwo wśród reguł składniowych tłumaczenia jest określone przez następujące fazy [patrz przypis] .

  1. fizyczne znaki plików źródłowych są mapowane, w sposób zdefiniowany w implementacji, do podstawowego zestawu znaków źródłowych (wprowadzenie znaków nowego wiersza dla wskaźników końca wiersza), jeżeli konieczne. [SNIP]
  2. każde wystąpienie odwrotnego ukośnika ( \ ), po którym następuje znak nowej linii, jest usuwane, łącząc fizyczne linie źródłowe z tworzą logiczne linie źródłowe. [SNIP]
  3. plik źródłowy jest rozkładany na tokeny wstępnego przetwarzania (2.5) i sekwencje znaków ze spacjami (w tym Komentarze). [SNIP]
  4. wykonywane są dyrektywy przetwarzania wstępnego, wywołania makr są rozszerzane, a operator _pragma unary wyrażenia są wykonywane. [SNIP]
  5. każdy element zestawu znaków źródłowych w literale lub łańcuchu znaków, a także każda sekwencja escape i uniwersalna-nazwa-znaku w literale znakowym lub nie-surowym ciągiem literalnym, jest konwertowany na odpowiadający element zestawu znaków wykonania; [SNIP]
  6. sąsiadujące ze sobą znaki ciągów literalnych są łączone.
  7. znaki oddzielające żetony nie mają już znaczenia. Każdy Token przetwarzania wstępnego jest konwertowany na token. (2.7). Na powstałe tokeny są analizowane składniowo i semantycznie oraz tłumaczone jako jednostka tłumaczeniowa. [SNIP]
  8. tłumaczone jednostki tłumaczeniowe i instancje są połączone w następujący sposób: [SNIP]
  9. wszystkie odniesienia do zewnętrznych encji są rozwiązane. Komponenty Biblioteki są połączone w celu zaspokojenia zewnętrznych odniesień do podmiotów nie zdefiniowanych w aktualne tłumaczenie. Wszystko takie Tłumacz wyjście jest zbierane do obraz programu, który zawiera informacje potrzebne do wykonania w swoim środowisko wykonawcze. (podkreślenie)

[footnote] implementacje muszą zachowywać się tak, jakby te oddzielne fazy występowały, chociaż w praktyce różne fazy mogą być złożone razem.

Określone błędy występują podczas ostatniego etapu kompilacji, najczęściej określanego jako linkowanie. To w zasadzie oznacza, że skompilowałeś kilka pliki implementacji do plików obiektowych lub bibliotek, a teraz chcesz, aby działały razem.

Powiedzmy, że zdefiniowałeś symbol a w a.cpp. Teraz, b.cpp zadeklarował Ten symbol i użył go. Przed połączeniem, po prostu zakłada, że ten symbol został zdefiniowany gdzieś , ale nie obchodzi go jeszcze gdzie. Faza łączenia jest odpowiedzialna za znalezienie symbolu i poprawne powiązanie go z b.cpp (właściwie z obiektem lub biblioteką, która go używa).

Jeśli jesteś za pomocą programu Microsoft Visual Studio zobaczysz, że projekty generują pliki .lib. Zawierają one tabelę eksportowanych symboli oraz tabelę importowanych symboli. Zaimportowane symbole są rozwiązywane względem bibliotek, z którymi się łączysz, a wyeksportowane symbole są dostarczane dla bibliotek, które używają tego .lib (jeśli istnieją).

Podobne mechanizmy istnieją dla innych kompilatorów / platform.

Typowe komunikaty o błędach to error LNK2001, error LNK1120, error LNK2019 dla Microsoft Visual Studio i undefined reference to symbol dla GCC.

Kod:

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

Wygeneruje następujące błędy z 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

I podobne błędy z 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

Najczęstsze przyczyny to:

 716
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-18 03:51:35

Członkowie klasy:

Czysty Destruktor wymaga implementacji.

Deklarowanie destruktora czystego nadal wymaga zdefiniowania go (w przeciwieństwie do zwykłej funkcji):

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

Dzieje się tak, ponieważ destruktory klas bazowych są wywoływane, gdy obiekt jest niszczony pośrednio, więc wymagana jest definicja.

virtual metody muszą być zaimplementowane lub zdefiniowane jako czyste.

Jest to podobne do metod nie virtual bez definicji, z dodanym rozumowanie, że deklaracja pure generuje atrapę Vtable i może pojawić się błąd linkera bez użycia funkcji:

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

Aby to zadziałało, zadeklaruj X::foo() jako czyste:

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

Nie-virtual Członkowie klasy

Niektóre elementy muszą być zdefiniowane, nawet jeśli nie są używane jawnie:

struct A
{ 
    ~A();
};

Następujący wynik byłby błędem:

A a;      //destructor undefined

Implementacja może być inline, w samej definicji klasy:

struct A
{ 
    ~A() {}
};

Lub Na Zewnątrz:

A::~A() {}

Jeśli implementacja znajduje się poza definicją klasy, ale w nagłówku, metody muszą być oznaczone jako inline, aby zapobiec definicji wielokrotnej.

Wszystkie używane metody prętowe muszą być zdefiniowane, jeśli są używane.

Częstym błędem jest zapominanie o zakwalifikowaniu nazwy:

struct A
{
   void foo();
};

void foo() {}

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

Definicja powinna być

void A::foo() {}

static elementy danych muszą być zdefiniowane poza klasą w jednostkowej jednostce translacyjnej:

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

An inicjalizator może być dostarczony dla static const element danych typu integral lub enumeration w ramach definicji klasy; jednak ODR-use tego elementu nadal będzie wymagało definicji zakresu przestrzeni nazw, jak opisano powyżej. C++11 umożliwia inicjalizację wewnątrz klasy dla wszystkich static const członków danych.

 154
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-09 01:06:32

Brak połączenia z odpowiednimi bibliotekami / plikami obiektowymi lub kompilacją plików implementacyjnych

Zwykle każda jednostka tłumaczenia generuje plik obiektowy zawierający definicje symboli zdefiniowanych w tej jednostce tłumaczenia. Aby użyć tych symboli, musisz połączyć się z tymi plikami obiektowymi.

Pod gcc można określić wszystkie pliki obiektowe, które mają być połączone ze sobą w wierszu poleceń, lub skompilować pliki implementacji razem.

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

The libraryName Oto tylko sama nazwa biblioteki, bez dodatków specyficznych dla platformy. Tak więc np. na Linuksie pliki bibliotek są zwykle nazywane libfoo.so, ale zapisywałbyś tylko -lfoo. W systemie Windows ten sam plik może być wywołany foo.lib, ale użyjesz tego samego argumentu. Być może trzeba będzie dodać katalog, w którym można znaleźć te pliki za pomocą -L‹directory›. Upewnij się, że nie zapisujesz spacji po -l lub -L.

For XCode : Dodaj ścieżkę wyszukiwania nagłówka użytkownika - > Dodaj ścieżka wyszukiwania biblioteki - > przeciągnij i upuść rzeczywiste odniesienie do biblioteki do folderu projektu.

Pod MSVS , pliki dodane do projektu są automatycznie połączone ze sobą, a plik lib zostanie wygenerowany (w powszechnym użyciu). Aby użyć symboli w oddzielnym projekcie, należy trzeba dołączyć pliki lib w ustawieniach projektu. Odbywa się to w sekcji Linker właściwości projektu, w Input -> Additional Dependencies. (ścieżka do pliku lib powinna być dodany w Linker -> General -> Additional Library Directories) W przypadku korzystania z biblioteki innej firmy, która jest dostarczana z plikiem lib, nieprzestrzeganie tego zwykle skutkuje błędem.

Może się również zdarzyć, że zapomnisz dodać plik do kompilacji, w takim przypadku plik obiektowy nie zostanie wygenerowany. W gcc dodajesz pliki do wiersza poleceń. W MSVS dodanie pliku do projektu spowoduje, że skompiluje go automatycznie(aczkolwiek pliki mogą być ręcznie wykluczone z kompilacji).

W Programowanie w systemie Windows, znak ostrzegawczy, że nie połączyłeś niezbędnej biblioteki, oznacza, że nazwa nierozwiązanego symbolu zaczyna się od __imp_. Poszukaj nazwy funkcji w dokumentacji i powinno być napisane, której biblioteki potrzebujesz użyć. Na przykład MSDN umieszcza informacje w polu u dołu każdej funkcji w sekcji o nazwie "Biblioteka".

 100
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-23 09:32:00

Zadeklarowana, ale nie zdefiniowana zmienna lub funkcja.

Typową deklaracją zmiennej jest

extern int x;

Ponieważ jest to tylko deklaracja, potrzebna jest pojedyncza definicja. Odpowiadająca jej definicja brzmiałaby:

int x;

Na przykład, następujące wywołałoby błąd:

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

Podobne uwagi dotyczą funkcji. Deklarowanie funkcji bez jej definiowania prowadzi do błędu:

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

Uważaj, aby zaimplementowana funkcja dokładnie pasowała do taką, którą zadeklarowałeś. Na przykład możesz mieć niedopasowane kwalifikatory 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)

Inne przykłady niedopasowania to

  • funkcja / zmienna zadeklarowana w jednej przestrzeni nazw, zdefiniowana w innej.
  • funkcja / zmienna zadeklarowana jako członek klasy, zdefiniowana jako globalna (lub odwrotnie).
  • Typ zwracanej funkcji, numer parametru i typy oraz konwencja wywołania nie zgadzają się ze sobą.

Komunikat o błędzie z kompilatora często daje pełną deklaracja zmiennej lub funkcji, która została zadeklarowana, ale nigdy nie zdefiniowana. Porównaj go dokładnie z podaną definicją. Upewnij się, że każdy szczegół pasuje.

 93
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-23 15:54:29

Kolejność, w jakiej są określone powiązane biblioteki, jest błędna.

Kolejność, w jakiej biblioteki są połączone, ma znaczenie, jeśli biblioteki są od siebie zależne. Ogólnie, jeśli Biblioteka A zależy od biblioteki B, to libA musi pojawić się przed libB w flagach linkera.

Na przykład:

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

Tworzenie bibliotek:

$ 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

Kompilacja:

$ 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

Więc powtórzę jeszcze raz, kolejność ma znaczenie!

 75
Author: Svalorzen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-10 11:46:08

Co to jest "undefined reference / unsolved external symbol"

Postaram się wyjaśnić, czym jest "undefined reference / unsolved external symbol".

Uwaga: używam g++ i Linuksa i wszystkie przykłady są dla niego

Na przykład mamy jakiś kod

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

I

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

Tworzenie plików obiektowych

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

Po fazie asemblera mamy plik obiektowy, który zawiera dowolne symbole do wyeksportowania. Zobacz też symbole

$ readelf --symbols src1.o
  Num:    Value          Size Type    Bind   Vis      Ndx Name
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL14local_var_name # [1]
     9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_var_name     # [2]

Odrzuciłem niektóre linie z wyjścia, ponieważ nie mają one znaczenia

Widzimy więc następujące symbole do eksportu.

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

Src2.cpp nie eksportuje niczego i nie widzieliśmy żadnych jego symboli

Połącz nasze pliki obiektowe

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

I uruchom go

$ ./prog
123

Linker widzi wyeksportowane Symbole i łączy je. Teraz staramy się odkomentować linie w src2.cpp jak tutaj

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

I odbudować plik obiektowy

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

OK (bez błędów), ponieważ my tylko zbuduj plik obiektu, linkowanie nie jest jeszcze zrobione. Spróbuj połączyć

$ 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

Stało się tak, ponieważ nasza local_var_name jest statyczna, tzn. nie jest widoczna dla innych modułów. Teraz głębiej. Uzyskaj wyjście fazy translacji

$ 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

Widzieliśmy, że nie ma etykiety dla local_var_name, dlatego linker jej nie znalazł. Ale jesteśmy hakerami:) i możemy to naprawić. Otwórz src1.s w edytorze tekstu i zmień

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

Do

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

Tzn. powinieneś mieć jak poniżej

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

Zmieniliśmy widoczność local_var_name i ustawiliśmy jej wartość na 456789. Spróbuj zbudować z niego plik obiektowy

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

Ok, patrz readelf wyjście (symbole)

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

Teraz local_var_name ma Bind globalny (był lokalny)

Link

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

I uruchom go

$ ./prog 
123456789

Ok, włamujemy się:)

Tak więc, w rezultacie - "undefined reference/unsolved external symbol error" dzieje się, gdy linker nie może znaleźć globalnych symboli w pliki obiektowe.

 65
Author: Kastaneda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-09 13:30:37

Symbole zostały zdefiniowane w programie C i użyte w kodzie C++.

Funkcja (lub zmienna) void foo() została zdefiniowana w programie C i próbujesz jej użyć w programie C++:

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

Linker C++ oczekuje, że nazwy będą zniekształcone, więc musisz zadeklarować funkcję jako:

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

Równoważnie, zamiast być zdefiniowane w programie C, funkcja (lub zmienna) void foo() została zdefiniowana w C++, ale z C linkage:

extern "C" void foo();

I próbujesz go użyć w programie C++ Z C++ linkage.

Jeśli cała Biblioteka jest dołączona do pliku nagłówkowego( i została skompilowana jako kod C); include musi być następujący;

extern "C" {
    #include "cheader.h"
}
 62
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-17 07:25:35

Jeśli Wszystko inne zawiedzie, przekompiluj.

Ostatnio udało mi się pozbyć nierozwiązanego błędu zewnętrznego w Visual Studio 2012 po prostu rekompilując plik. Kiedy przebudowałem, błąd zniknął.

Zwykle dzieje się tak, gdy dwie (lub więcej) biblioteki mają cykliczną zależność. Biblioteka A próbuje używać symboli w B. lib, a biblioteka B próbuje używać symboli z A. lib. Żadne z nich nie istnieje. Podczas próby skompilowania A, krok łącza zakończy się niepowodzeniem, ponieważ nie może znaleźć B. lib. A. lib zostanie wygenerowany, ale nie dll. Następnie kompilujesz B, które zakończy się sukcesem i generujesz B. lib. Ponowne kompilowanie A będzie teraz działać, ponieważ B. lib jest teraz znaleziony.

 59
Author: sgryzko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-03 20:56:14

Niepoprawne importowanie / eksportowanie metod / klas między modułami / dll (specyficzne dla kompilatora).

MSVS wymaga określenia, które symbole mają być eksportowane i importowane za pomocą __declspec(dllexport) i __declspec(dllimport).

Ta Podwójna funkcjonalność jest zwykle uzyskiwana poprzez użycie makra:

#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif

Makro THIS_MODULE będzie zdefiniowane tylko w module, który eksportuje funkcję. W ten sposób deklaracja:

DLLIMPEXP void foo();

Rozszerza się do

__declspec(dllexport) void foo();

I każe kompilatorowi wyeksportować funkcji, ponieważ obecny moduł zawiera jego definicję. Gdy deklaracja zostanie włączona do innego modułu, rozszerzy się ona do

__declspec(dllimport) void foo();

I mówi kompilatorowi, że definicja znajduje się w jednej z bibliotek, z którymi się połączyłeś (Zobacz też 1)).

Możesz importować/eksportować podobne klasy:

class DLLIMPEXP X
{
};
 51
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 06:26:07

Implementacje szablonu nie są widoczne.

Nieokreślone szablony muszą mieć swoje definicje widoczne dla wszystkich jednostek tłumaczeniowych, które ich używają. Oznacza to, że nie można oddzielić definicji szablonu do pliku implementacyjnego. Jeśli musisz oddzielić implementację, Zwykle obejściem jest posiadanie pliku impl, który dołączasz na końcu nagłówka, który deklaruje szablon. Typowa sytuacja to:

template<class T>
struct X
{
    void foo();
};

int main()
{
    X<int> x;
    x.foo();
}

//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}

Aby to naprawić, musisz przenieść definicję X::foo do plik nagłówkowy lub jakieś miejsce widoczne dla jednostki tłumaczeniowej, która go używa.

Specjalistyczne szablony mogą być zaimplementowane w pliku implementacji i implementacja nie musi być widoczna, ale specjalizacja musi być wcześniej zadeklarowana.

Aby uzyskać dalsze wyjaśnienie i inne możliwe rozwiązanie (Jawna instancja) zobacz to pytanie i odpowiedź .

 49
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:46

Jest to jeden z najbardziej mylących komunikatów o błędach, które każdy programista VC++ widział raz po raz. Najpierw wyjaśnijmy to sobie.

A. Co to jest symbol? W skrócie, symbol to nazwa. Może to być nazwa zmiennej, nazwa funkcji, nazwa klasy, Nazwa typedef lub cokolwiek innego z wyjątkiem tych nazw i znaków, które należą do języka C++. Jest definiowana przez użytkownika lub wprowadzana przez Bibliotekę zależności (inną zdefiniowaną przez użytkownika).

B. Co to jest zewnętrzne? W VC++, każdy plik źródłowy (.cpp,c, itp.) jest uważany za jednostkę tłumaczenia, kompilator kompiluje po jednej jednostce na raz i generuje jeden plik obiektowy(.obj) dla aktualnej jednostki tłumaczeniowej. (Zauważ, że każdy plik nagłówkowy, który zawiera ten plik źródłowy, zostanie wstępnie przetworzony i będzie uważany za część tej jednostki tłumaczeniowej)wszystko w jednostce tłumaczeniowej jest uważane za wewnętrzne, Wszystko inne jest uważane za zewnętrzne. W C++ można odwoływać się do symbolu zewnętrznego za pomocą słów kluczowych takich jak extern, __declspec (dllimport) i tak dalej.

C. Co to jest "resolve"? Resolve to termin łączący czas. W linking-time, linker próbuje znaleźć zewnętrzną definicję każdego symbolu w plikach obiektowych, które nie mogą znaleźć swojej definicji wewnętrznie. Zakres tego procesu wyszukiwania, w tym:

  • wszystkie pliki obiektowe wygenerowane w czasie kompilacji
  • wszystkie biblioteki (.lib) które są jawnie lub pośrednio określone jako dodatkowe zależności tego budynku podanie.

Ten proces wyszukiwania nazywa się resolve.

D. wreszcie, dlaczego nierozwiązany Symbol zewnętrzny? Jeśli łącznik nie może znaleźć zewnętrznej definicji symbolu, który nie ma wewnętrznej definicji, zgłasza nierozwiązany zewnętrzny błąd symbolu.

E. możliwe przyczyny lnk2019 : nierozwiązany zewnętrzny błąd symbolu. Wiemy już, że ten błąd jest spowodowany tym, że linker nie znalazł definicji symboli zewnętrznych, możliwych przyczyn można sortować jako:

  1. definicja istnieje

Na przykład, jeśli mamy funkcję o nazwie foo zdefiniowaną w a.cpp:

int foo()
{
    return 0;
}

W b.cpp chcemy wywołać funkcję foo, więc dodajemy

void foo();

Aby zadeklarować funkcję foo () i wywołać ją w innym ciele funkcji, powiedzmy bar():

void bar()
{
    foo();
}

Teraz, gdy zbudujesz ten kod, otrzymasz błąd LNK2019 narzekający, że foo jest nierozwiązanym symbolem. W tym przypadku wiemy, że foo () ma swoją definicję w a.cpp, ale inaczej od tej, którą wywołujemy(inna wartość zwracana). W tym przypadku definicja istnieje.

  1. definicja nie istnieje

Jeśli chcemy wywołać niektóre funkcje w bibliotece, ale biblioteka importu nie jest dodawana do dodatkowej listy zależności (ustawionej od: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency) w ustawieniach projektu. Teraz linker zgłosi lnk2019, ponieważ definicja nie istnieje w bieżącym zakresie wyszukiwania.

 48
Author: Nima Soroush,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-08 09:05:14

Undefined reference to WinMain@16 or similar "unusual' main() entry point reference (szczególnie dla visual-studio ).

Być może nie wybrałeś odpowiedniego typu projektu z Twoim rzeczywistym IDE. IDE może chcieć powiązać np. projekty aplikacji Windows z taką funkcją punktu wejścia (określoną w brakującym odnośniku powyżej), zamiast powszechnie używanego podpisu int main(int argc, char** argv);.

Jeśli Twoje IDE obsługuje zwykłe projekty konsoli możesz chcieć wybierz ten typ projektu zamiast projektu aplikacji windows.


Oto case1 i case2 omówione bardziej szczegółowo zrealnego świata problemu.

 34
Author: πάντα ῥεῖ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-12 17:39:25

Jeśli korzystasz z bibliotek innych firm, Upewnij się, że masz poprawne 32/64-bitowe pliki binarne

 32
Author: Dula,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 17:06:24

Microsoft oferuje #pragma, aby odwoływać się do poprawnej biblioteki w czasie łącza;

#pragma comment(lib, "libname.lib")

Oprócz ścieżki biblioteki zawierającej katalog biblioteki, powinna to być pełna nazwa biblioteki.

 32
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-10 06:37:35

Pakiet Visual Studio NuGet wymaga aktualizacji dla nowej wersji zestawu narzędzi

Właśnie miałem problem z połączeniem libpng z Visual Studio 2013. Problem polega na tym, że plik pakietu miał tylko biblioteki dla Visual Studio 2010 i 2012.

Poprawnym rozwiązaniem jest mieć nadzieję, że deweloper wyda zaktualizowany pakiet, a następnie uaktualni, ale zadziałało to dla mnie, hakując w dodatkowym ustawieniu dla VS2013, wskazując na pliki biblioteki VS2012.

Edytowałem pakiet (w folderze packages wewnątrz katalogu rozwiązania) znajdując packagename\build\native\packagename.targets i wewnątrz tego pliku, kopiując wszystkie sekcje v110. Zmieniłem v110 na v120 w tylko pola warunkowe uważając, aby pozostawić wszystkie ścieżki nazw plików jako v110. To po prostu pozwoliło Visual Studio 2013 połączyć się z bibliotekami na rok 2012 i w tym przypadku zadziałało.

 31
Author: Malvineous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-28 03:52:36

Załóżmy, że masz duży projekt napisany w c++, który ma tysiąc .plików cpp i tysiąc .pliki H.I powiedzmy, że projekt zależy również od dziesięciu bibliotek statycznych. Powiedzmy, że jesteśmy na Windows i budujemy nasz projekt w Visual Studio 20xx. po naciśnięciu Ctrl + F7 Visual Studio, aby rozpocząć kompilację całego rozwiązania ( Załóżmy, że mamy tylko jeden projekt w rozwiązaniu)

Co oznacza kompilacja ?

  • Visual Studio szukaj w pliku .vcxproj i rozpocząć kompilację KAŻDEGO pliku, który ma rozszerzenie .cpp. Kolejność kompilacji to undefined.So nie należy zakładać, że plik główny.cpp jest kompilowany jako pierwszy
  • Jeśli .pliki cpp zależą od dodatkowych .pliki h W celu znalezienia symboli które mogą lub nie mogą być zdefiniowane w pliku .cpp
  • Jeśli istnieje .plik cpp, w którym kompilator nie mógł znaleźć jednego symbolu, błąd czasu kompilatora podnosi komunikat Symbol x nie może być znaleziono
  • dla każdego pliku z rozszerzeniem .cpp jest generowany plik obiektowy .o, a także Visual Studio zapisuje dane wyjściowe w pliku o nazwie ProjectName.Cpp.Czysto.txt zawierający wszystkie pliki obiektowe, które muszą być przetwarzane przez łącznik.

Drugi etap kompilacji jest wykonywany przez linkera.Linker powinien scalić cały plik obiektowy i ostatecznie zbudować wyjście (które może być wykonywalne lub biblioteką)

Kroki w linkowaniu projektu

  • Parsuj wszystkie pliki obiektowe i znajdź definicję, która została zadeklarowana tylko w nagłówkach (np: kod jednej metody klasy, jak wspomniano w poprzednich odpowiedziach, lub zdarzenie inicjalizacji zmiennej statycznej, która jest członkiem wewnątrz klasy)
  • Jeśli nie można znaleźć jednego symbolu w plikach obiektowych, jest on również wyszukiwany w dodatkowych bibliotekach.Aby dodać nową bibliotekę do projektu właściwości konfiguracyjne -> katalogi VC++ -> Biblioteka Katalogi i tutaj podałeś dodatkowy folder do wyszukiwania bibliotek i właściwości konfiguracji -> Linker -> Input do określenia nazwy biblioteki. - Jeśli Linker nie może znaleźć symbolu, który piszesz w jednym .cpp podnosi błąd czasu linkera , który może brzmieć jak error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)

Obserwacja

  1. gdy Linker znajdzie jeden symbol, nie szuka go w innych bibliotekach
  2. kolejność linkowanie bibliotek ma znaczenie.
  3. Jeśli Linker znajdzie zewnętrzny symbol w jednej statycznej bibliotece, zamieści on Ten symbol w wyjściu projektu.Jednak, jeśli Biblioteka jest współdzielona (dynamiczna), nie zawiera kodu (symboli ) w wyjściu, ale Run-Time mogą wystąpić awarie

Jak rozwiązać ten rodzaj błędu

Błąd Czasu Kompilatora:

  • upewnij się, że poprawnie napisałeś składnię swojego projektu c++.

Linker Błąd Czasu

  • Zdefiniuj wszystkie symbole, które deklarujesz w plikach nagłówkowych
  • użyj #pragma once, aby umożliwić kompilatorowi nie dołączanie jednego nagłówka, jeśli był już zawarty w bieżącym .cpp, które są kompilowane
  • upewnij się, że zewnętrzna biblioteka nie zawiera symboli, które mogą wchodzić w konflikt z innymi symbolami zdefiniowanymi w plikach nagłówkowych
  • Gdy używasz szablonu, aby upewnić się, że w nagłówku umieścisz definicję każdej funkcji szablonu plik pozwalający kompilatorowi na wygenerowanie odpowiedniego kodu dla dowolnych instancji.
 30
Author: eFarzad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-08 10:15:57

Błąd w kompilatorze / IDE

Ostatnio miałem ten problem i okazało się, że to błąd w Visual Studio Express 2013 . Musiałem usunąć plik źródłowy z projektu i dodać go ponownie, aby przezwyciężyć błąd.

Kroki, aby spróbować, jeśli uważasz, że może to być błąd w kompilatorze / IDE:

  • Wyczyść projekt (niektóre IDE mają taką możliwość, możesz także można to zrobić ręcznie, usuwając pliki obiektowe)
  • spróbuj rozpocząć nowy projekt, kopiowanie całego kodu źródłowego z oryginał.
 26
Author: developerbmw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:30

Połączone .plik lib jest skojarzony zdll

Miałem ten sam problem. Powiedzmy, że mam projekty MyProject i TestProject. Skutecznie połączyłem plik lib dla MyProject z TestProject. Jednak ten plik lib został wyprodukowany jako DLL dla MyProject został zbudowany. Ponadto, nie zawierałem kodu źródłowego dla wszystkich metod w MyProject, ale tylko dostęp do punktów wejściowych DLL.

Aby rozwiązać problem, zbudowałem MyProject jako LIB i połączyłem TestProject z tym .lib plik (kopiuję wklej wygenerowany .plik lib do folderu TestProject). Następnie mogę ponownie zbudować MyProject jako DLL. Jest kompilowany, ponieważ lib, do którego jest podłączony TestProject zawiera kod dla wszystkich metod w klasach w MyProject.

 23
Author: octoback,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-04 15:02:33

Użyj linkera, aby pomóc zdiagnozować błąd

Większość nowoczesnych linkerów zawiera opcję verbose, która drukuje w różnym stopniu;

  • wywołanie linku (wiersz poleceń),
  • Dane o tym, jakie biblioteki są zawarte w etapie łącza,
  • lokalizacja bibliotek,
  • używane ścieżki wyszukiwania.

Dla gcc i clang; zazwyczaj dodajesz -v -Wl,--verbose LUB -v -Wl,-v do wiersza poleceń. Więcej szczegółów można znaleźć tutaj;

Dla MSVC, /VERBOSE (w szczególności /VERBOSE:LIB) jest dodawany do wiersza poleceń łącza.

 23
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-24 10:41:08

Ponieważ ludzie wydają się być kierowani na to pytanie, jeśli chodzi o błędy linkera, dodam to tutaj.

Jedną z możliwych przyczyn błędów linkera w GCC 5.2.0 jest to, że nowa biblioteka libstdc++ ABI jest teraz domyślnie wybierana.

Jeśli pojawią się błędy linkera dotyczące niezdefiniowanych odniesień do symboli zawierających typy w przestrzeni nazw std::_ _ cxx11 lub znaczniku [abi :cxx11], prawdopodobnie oznacza to, że próbujesz połączyć ze sobą pliki obiektowe, które zostały skompilowane różne wartości dla makra _glibcxx_use_cxx11_abi. Dzieje się tak często, gdy łączy się z biblioteką innej firmy, która została skompilowana ze starszą wersją GCC. Jeśli Biblioteka innej firmy nie może zostać odbudowana za pomocą nowego ABI, będziesz musiał przekompilować swój kod za pomocą starego ABI.

Więc jeśli nagle pojawią się błędy linkera podczas przełączania na GCC po 5.1.0, to będzie to rzecz do sprawdzenia.

 18
Author: Plankalkül,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-10 11:03:35

Pakiet GNU LD, który nie obsługuje skryptów linkera

Niektóre pliki. so to w rzeczywistości Skrypty linkera GNU LD , np. libtbb.so Plik jest plikiem tekstowym ASCII o tej zawartości:

INPUT (libtbb.so.2)

Niektóre bardziej złożone Kompilacje mogą tego nie wspierać. Na przykład, jeśli dodasz opcję-v do opcji kompilatora, możesz zobaczyć, że mainwin GCC wrapper mwdip odrzuca pliki poleceń skryptu linkera na obszernej liście bibliotek do linkowania. A proste obejście polega na zastąpieniu pliku wejściowego skryptu linkera kopią pliku (lub dowiązaniem symbolicznym), np.

cp libtbb.so.2 libtbb.so

Lub możesz zastąpić argument-l pełną ścieżką .so, np. zamiast -ltbb do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2

 17
Author: JDiMatteo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-30 20:47:37

Zaprzyjaźnione szablony...

Biorąc pod uwagę fragment kodu typu szablonu z operatorem friend (lub funkcją);

template <typename T>
class Foo {
    friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};

operator<< jest zadeklarowana jako funkcja nie-szablonowa. Dla każdego typu T używanego z Foo, musi być nie-template operator<<. Na przykład, jeśli zadeklarowany jest typ Foo<int>, to musi istnieć implementacja operatora w następujący sposób:

std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}

Ponieważ nie jest zaimplementowany, linker nie znajduje go i skutkuje błąd.

Aby to poprawić, możesz zadeklarować operator szablonu przed typem Foo, a następnie zadeklarować jako przyjaciel odpowiednią instancję. Składnia jest trochę niezręczna, ale wygląda następująco;

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

Powyższy kod ogranicza przyjaźń operatora do odpowiedniej instancji Foo, tzn. instancja operator<< <int> jest ograniczona do dostępu do prywatnych członków instancji Foo<int>.

Alternatywy include;

  • Pozwalając na rozszerzenie przyjaźni na wszystkie instancje szablonów, w następujący sposób;

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    
  • Lub implementacja dla operator<< może być wykonana w linii wewnątrz definicji klasy;

    template <typename T>
    class Foo {
        friend std::ostream& operator<<(std::ostream& os, const Foo& a)
        { /*...*/ }
        // ...
    };
    

Uwaga, gdy deklaracja operatora (lub funkcji) pojawia się tylko w klasie, nazwa nie jest dostępna dla" normalnego " wyszukiwania, tylko dla wyszukiwania zależnego od argumentów, z cppreference;

Nazwa po raz pierwszy zadeklarowana w deklaracji znajomego w obrębie klasy lub szablonu klasy X staje się członkiem najbardziej wewnętrznej przestrzeni nazw X, ale nie jest dostępna dla wyszukiwania (z wyjątkiem wyszukiwania zależnego od argumentów, które uwzględnia X), chyba że zostanie dostarczona pasująca deklaracja w zakresie przestrzeni nazw...

Jest więcej informacji na temat template friends na cppreference i C++ FAQ .

Lista kodów przedstawiająca techniki powyżej .


g++ ostrzega o tym w następujący sposób

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
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-10 21:29:24

Niespójne UNICODE definicje

Windows Unicode build jest zbudowany z TCHAR itd. zdefiniowane jako wchar_t itd. Gdy nie budujemy z UNICODE zdefiniowanym jako budujemy z TCHAR zdefiniowanym jako char itd. Te UNICODE i _UNICODE definiują wszystkie "T" typy łańcuchów; LPTSTR, LPCTSTR i ich łosie.

Zbudowanie jednej biblioteki z UNICODE zdefiniowanej i próba połączenia jej w projekcie, w którym UNICODE nie jest zdefiniowana, spowoduje błędy linkera, ponieważ będzie niedopasowanie w definicji TCHAR; char vs. wchar_t.

Błąd zwykle zawiera wartość funkcji z typem pochodnym char lub wchar_t, mogą one obejmować std::basic_string<> itp. też. Podczas przeglądania dotkniętej funkcji w kodzie często pojawia się odniesienie do TCHAR lub std::basic_string<TCHAR> itp. Jest to znak ostrzegawczy, że kod pierwotnie był przeznaczony zarówno do budowy UNICODE, jak i wielobajtowego znaku (lub "wąskiej").

Aby to poprawić, zbuduj wszystkie wymagane biblioteki i projekty o spójnej definicji UNICODE (i _UNICODE).

  1. Można to zrobić za pomocą obu;
    #define UNICODE
    #define _UNICODE
    
  2. Lub w ustawieniach projektu;

    Właściwości Projektu > Ogólne > Domyślne Wartości Projektu > Zestaw Znaków

  3. Lub w wierszu poleceń;

    /DUNICODE /D_UNICODE
    

Alternatywa ma również zastosowanie, jeśli UNICODE nie jest przeznaczony do użycia, upewnij się, że defines nie są ustawione i / lub multi-znak ustawienie jest wykorzystywane w projektach i konsekwentnie stosowane.

Nie zapominaj o spójności pomiędzy kompilacjami" Release "i" Debug".

 13
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-12 06:06:08

Clean and rebuild

"czysty" kompilacji może usunąć "martwe drewno", które może być pozostawione z poprzednich kompilacji, nieudanych kompilacji, niekompletnych kompilacji i innych problemów związanych z kompilacją systemu.

Ogólnie IDE lub build będzie zawierać jakąś formę" czystej " funkcji, ale może to nie być poprawnie skonfigurowane (np. w ręcznym pliku makefile) lub może się nie udać (np. pośrednie lub wynikowe binaria są tylko do odczytu).

Po zakończeniu "clean" sprawdź, czy "czyste" powiodło się i cały wygenerowany plik pośredni (np. zautomatyzowany plik makefile) został pomyślnie usunięty.

Ten proces może być postrzegany jako ostateczność, ale często jest dobrym pierwszym krokiem ; zwłaszcza jeśli kod związany z błędem został niedawno dodany (lokalnie lub z repozytorium źródłowego).

 12
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-24 12:41:09

Twoje połączenie zużywa biblioteki przed plikami obiektowymi, które się do nich odnoszą

  • próbujesz skompilować i połączyć swój program z łańcuchem narzędzi GCC.
  • Twoje połączenie określa wszystkie niezbędne biblioteki i ścieżki wyszukiwania bibliotek
  • jeśli libfoo zależy od libbar, to twoje połączenie poprawnie umieszcza libfoo przed libbar.
  • Twoje połączenie nie działa z undefined reference to coś błędy.
  • ale wszystkie nieokreślone coś s są deklarowane w plikach nagłówkowych, które posiadasz #included i są w rzeczywistości zdefiniowane w bibliotekach, które łączysz.

Przykłady są w C. równie dobrze mogą być C++

W 2004 roku, w ramach projektu, powstała Biblioteka statyczna.]}

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

Budujesz swoją bibliotekę statyczną:

$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o

Kompilujesz swoje program:

$ gcc -I. -c -o eg1.o eg1.c

Próbujesz połączyć go z libmy_lib.a i nie powiodło się:

$ 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

Ten sam wynik jeśli skompilujesz i połączysz w jednym kroku, jak:

$ 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
Biblioteka kompresji (ang. compression library) - Biblioteka kompresji systemu współdzielonego.]}

Eg2.c

#include <zlib.h>
#include <stdio.h>

int main()
{
    printf("%s\n",zlibVersion());
    return 0;
}

Skompiluj swój program:

$ gcc -c -o eg2.o eg2.c

Spróbuj połączyć swój program z libz i nie uda:

$ 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

To samo jeśli kompilujesz i linkujesz za jednym razem:

$ 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

I a wariacja na przykładzie 2 z udziałem pkg-config:

$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
Co robisz źle?

W sekwencji plików obiektowych i bibliotek, które chcesz połączyć, aby Twoje program, umieszczasz biblioteki przed plikami obiektowymi, które odnoszą się do oni. Musisz umieścić biblioteki po pliki obiektowe, które odnoszą się za nich.

Przykład linku 1 poprawnie:

$ gcc -o eg1 eg1.o -L. -lmy_lib

Sukces:

$ ./eg1 
Hello World

Przykład linku 2 poprawnie:

$ gcc -o eg2 eg2.o -lz

Sukces:

$ ./eg2 
1.2.8

Podlinkuj poprawnie przykład 2 pkg-config:

$ gcc -o eg2 eg2.o $(pkg-config --libs zlib) 
$ ./eg2
1.2.8

Wyjaśnienie

czytanie jest opcjonalne od teraz .

Domyślnie, polecenie linkage generowane przez GCC, na twojej dystrybucji, pochłania pliki w linku od lewej do prawej w Sekwencja wiersza poleceń. Gdy okaże się, że plik odnosi się do czegoś i nie zawiera dla niego definicji, aby szukać definicja w plikach po prawej stronie. Jeśli w końcu znajdzie definicję, Referencja rozwiązana. Jeśli jakiekolwiek odniesienia pozostaną nierozwiązane na końcu, połączenie nie powiodło się: łącznik nie wyszukuje wstecz.

Pierwszy, przykład 1 , ze statyczną biblioteką my_lib.a

Statyczna Biblioteka jest indeksowanym archiwum plików obiektowych. Gdy linker znajduje -lmy_lib w sekwencji połączeń i stwierdza, że odnosi się to do biblioteki statycznej ./libmy_lib.a, chce wiedzieć, czy Twój program wymaga dowolnego z plików obiektowych w libmy_lib.a.

W libmy_lib.a jest tylko plik obiektowy, a mianowicie my_lib.o i jest tylko jedna rzecz zdefiniowana w my_lib.o, czyli funkcji hw.

Linker zdecyduje, że twój program potrzebuje my_lib.o wtedy i tylko wtedy, gdy już wie, że Twój program odnosi się do hw, w jednym lub kilku plikach obiektowych, które już posiada dodany do programu, i że żaden z plików obiektowych, które już dodał zawiera definicję dla hw.

Jeśli to prawda, to linker wyodrębni kopię my_lib.o z biblioteki i dodaj go do swojego programu. Następnie twój program zawiera definicję hw, więc jego odwołania do hwrozwiązane .

Gdy próbujesz połączyć program w taki sposób:

$ gcc -o eg1 -L. -lmy_lib eg1.o

Linker nie dodał eg1.o do programu kiedy widzi -lmy_lib. Ponieważ w tym momencie nie widział eg1.o. Twój program nie tworzy jeszcze żadnych odniesienia do hw: it nie czyni jeszcze żadnych odniesień w ogóle , ponieważ wszystkie odniesienia, które czyni są w eg1.o.

Więc linker nie dodaje my_lib.o do programu i nie ma już Użyj dla libmy_lib.a.

Następnie znajduje eg1.o i dodaje go do programu. Plik obiektowy w Sekwencja połączeń jest zawsze dodawana do programu. Teraz program sprawia, że odniesienie do hw i nie zawiera definicji hw; ale nic nie zostało w linku sekwencja, która może dostarczyć brakujące definicja. Odniesienie do hw kończy się nierozwiązane , a połączenie nie działa.

Drugi, przykład 2 , z biblioteką współdzieloną libz

Biblioteka współdzielona nie jest archiwum plików obiektowych ani niczego podobnego. On o wiele bardziej jak program, który nie posiada main funkcji i zamiast tego eksponuje wiele innych symboli, które definiuje, tak że inne programy mogą z nich korzystać w runtime.

Wiele dystrybucji Linuksa konfiguruje dziś swój łańcuch narzędzi GCC tak, aby jego sterowniki językowe(gcc,g++,gfortran itd) poinstruuj system linker (ld), aby łączył biblioteki współdzielone na podstawie w razie potrzeby. Masz jednego z tych DISTRO.

Oznacza to, że gdy linker znajdzie -lz w sekwencji połączeń i wykaże, że odnosi się to do biblioteki współdzielonej (powiedzmy) /usr/lib/x86_64-linux-gnu/libz.so, chce wiedzieć, czy jakieś odniesienia zostały dodane do programu te, które nie są jeszcze zdefiniowane, mają definicje, które są eksportowane przez libz

Jeśli to prawda, to linker nie skopiuje dowolne kawałki z libz i dodaj je do swojego programu; zamiast tego będzie to po prostu doctor kod programu tak że: -

  • W czasie wykonywania programu systemowego loader załaduje kopię libz do ten sam proces jak Twój program, gdy ładuje kopię Twojego programu, aby go uruchomić.

  • W czasie wykonywania, gdy Twój program odnosi się do czegoś, co jest zdefiniowane w libz, odniesienie to wykorzystuje definicję wyeksportowaną przez kopię libz w ten sam proces.

Twój program chce odnosić się tylko do jednej rzeczy, która ma definicję wyeksportowaną przez libz, mianowicie funkcja zlibVersion, o której mowa tylko raz, w eg2.c. Jeśli linker doda to odniesienie do twojego programu, a następnie znajdzie definicję eksportowane przez libz, Referencja to

Ale przy próbie połączenia programu w taki sposób:

gcc -o eg2 -lz eg2.o

Kolejność zdarzeń jest nieprawidłowa w taki sam sposób jak w przykładzie 1. W momencie gdy linker znajdzie -lz, nie ma żadnych odniesień do czegokolwiek w programie: wszystkie są w eg2.o, czego jeszcze nie było widać. Tak więc linker decyduje, że nie ma zastosowania do libz. Gdy osiągnie eg2.o, dodaje go do programu, i wtedy ma niezdefiniowaną referencję do zlibVersion, Sekwencja połączeń jest zakończona; to odniesienie jest nierozwiązane, i połączenie nie działa.

Wreszcie, pkg-config odmiana przykładu 2 ma teraz oczywiste Wyjaśnienie. Po rozszerzeniu powłoki:

gcc -o eg2 $(pkg-config --libs zlib) eg2.o

Staje się:

gcc -o eg2 -lz eg2.o

Czyli znowu przykład 2.

Mogę odtworzyć problem w przykładzie 1, ale nie w przykładzie 2

Związek:

gcc -o eg2 -lz eg2.o

Działa dobrze dla Ciebie!

W przeciwieństwie do poprzednich wersji, nie jest to możliwe.]}

To ponieważ distro, na którym działa linkage, jest jednym z tych, które nie konfiguruje swojego łańcucha narzędzi GCC do łączenia bibliotek współdzielonych w razie potrzeby.

Kiedyś, w systemach uniksopodobnych normalne było łączenie statycznych i współdzielonych biblioteki według różnych zasad. Biblioteki statyczne w sekwencji linków zostały połączone na podstawie as-needed wyjaśnione w przykładzie 1, ale biblioteki współdzielone zostały połączone bezwarunkowo.

[[110]}to zachowanie jest ekonomiczne w linktime bo linker nie musi się zastanawiać czy program potrzebuje biblioteki współdzielonej: czy jest to biblioteka współdzielona, połącz to. Większość bibliotek w większości łączy to biblioteki współdzielone. Ale są też wady:-
  • Jest nieekonomiczny w środowisku runtime , ponieważ może spowodować, że biblioteki współdzielone będą ładowane wraz z programem, nawet jeśli ich nie potrzebuje.

  • Różne reguły łączenia bibliotek statycznych i współdzielonych mogą być mylące do niedoświadczonych programistów, którzy mogą nie wiedzieć, czy [85] w swoim powiązaniu ma zamiar rozwiązać do /some/where/libfoo.a lub do /some/where/libfoo.so, i może nie zrozumieć różnicy między bibliotekami współdzielonymi i statycznymi w każdym razie.

Ten kompromis doprowadził do dzisiejszej sytuacji schizmatycznej. Niektóre DISTRO mają zmiany zasad łączenia GCC dla bibliotek współdzielonych tak, aby jako-potrzebne zasada dotyczy wszystkich bibliotek. Niektóre DISTRO utknęły z stare sposób.

Dlaczego nadal mam ten problem, nawet jeśli kompiluję i łączę w tym samym czasie?

If I just do:

$ gcc -o eg1 -I. -L. -lmy_lib eg1.c

Z pewnością gcc musi najpierw skompilować eg1.c, a następnie połączyć wynikowe plik obiektowy z libmy_lib.a. Więc jak może nie wiedzieć, że plik obiektu jest potrzebny, gdy robi linkowanie?

Ponieważ kompilowanie i linkowanie jednym poleceniem nie zmienia kolejność sekwencji połączeń.

Po uruchomieniu powyższego polecenia, gcc domyśla się, że chcesz kompilacji + linkage. Więc za kulisami generuje polecenie kompilacji i uruchamia następnie generuje polecenie linkage i uruchamia je, tak jakby ty uruchomiłeś dwie komendy:

$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o

Więc połączenie nie powiedzie się tak samo, jak jeśli wykonasz Uruchom te dwie komendy. Na jedyną różnicą, którą zauważysz w niepowodzeniu, jest to, że gcc wygenerowało tymczasowy plik obiektowy w przypadku compile + link, ponieważ nie mówisz o tym do użycia eg1.o. My zobacz:

/tmp/ccQk1tvs.o: In function `main'

Zamiast:

eg1.o: In function `main':

Zobacz też

Kolejność, w jakiej określone są powiązane biblioteki jest błędna

Umieszczenie współzależnych bibliotek w niewłaściwej kolejności jest tylko jednym ze sposobów w którym można uzyskać pliki, które potrzebują definicji rzeczy nadchodzących później w linkage niż pliki, które dostarczyć definicje. Stawianie bibliotek przed pliki obiektowe, które się do nich odnoszą, to inny sposób popełniając ten sam błąd.

 11
Author: Mike Kinghan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:53

Gdy ścieżki dołączania są różne

Błędy linkera mogą wystąpić, gdy plik nagłówka i powiązana z nim biblioteka współdzielona (.plik lib) go out of sync. Pozwól mi wyjaśnić.

Jak działają linkery? Linker dopasowuje deklarację funkcji (zadeklarowaną w nagłówku) z jej definicją (w bibliotece współdzielonej), porównując ich sygnatury. Możesz uzyskać błąd linkera, jeśli linker nie znajdzie definicji funkcji, która idealnie pasuje.

Czy można jeszcze uzyskać błąd linkera, mimo że deklaracja i definicja wydają się pasować? Tak! Mogą wyglądać tak samo w kodzie źródłowym, ale tak naprawdę zależy to od tego, co widzi kompilator. W zasadzie możesz skończyć z taką sytuacją:

// header1.h
typedef int Number;
void foo(Number);

// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically

Zauważ, że chociaż obie deklaracje funkcji wyglądają identycznie w kodzie źródłowym, to różnią się one w zależności od kompilatora.

[13]}można zapytać, jak jeden kończy się w takiej sytuacji? Dołącz ścieżki oczywiście! Jeśli podczas kompilacji biblioteki współdzielonej ścieżka dołączania prowadzi do header1.h i kończy się używaniem header2.h w swoim własnym programie, będziesz drapał nagłówek i zastanawiał się, co się stało (zamierzona gra słów).

Przykład jak to może się zdarzyć w świecie rzeczywistym jest wyjaśniony poniżej.

Dalsze opracowanie na przykładzie

Mam dwa projekty: graphics.lib i main.exe. Oba projekty zależą od common_math.h. Załóżmy, że biblioteka eksportuje następującą funkcję:

// graphics.lib    
#include "common_math.h" 

void draw(vec3 p) { ... } // vec3 comes from common_math.h

A potem ty Dołącz bibliotekę do własnego projektu.

// main.exe
#include "other/common_math.h"
#include "graphics.h"

int main() {
    draw(...);
}

Boom! Dostajesz błąd linkera i nie masz pojęcia, dlaczego zawodzi. Powodem jest to, że wspólna biblioteka używa różnych wersji tego samego include common_math.h (w przykładzie pokazałem, że jest to oczywiste, włączając inną ścieżkę, ale nie zawsze może być tak oczywiste. Być może ścieżka dołączania jest inna w Ustawieniach kompilatora).

Uwaga w tym przykładzie linker powie Ci, że nie może znaleźć draw(), gdy w rzeczywistości wiesz, że jest to oczywiście eksportowane przez Bibliotekę. Możesz całymi godzinami drapać się po głowie, zastanawiając się, co poszło nie tak. Chodzi o to, że linker widzi inną sygnaturę, ponieważ typy parametrów są nieco inne. W przykładzie, vec3 jest innym typem w obu projektach, jeśli chodzi o kompilator. Może się to zdarzyć, ponieważ pochodzą one z dwóch nieco różnych plików nagłówkowych (być może pliki nagłówkowe pochodzą z dwóch różnych wersji biblioteka).

Debugowanie linkera

DUMPBIN jest twoim przyjacielem, jeśli używasz Visual Studio. Jestem pewien, że inne Kompilatory mają inne podobne narzędzia.

Proces przebiega następująco:

  1. zwróć uwagę na dziwną zniekształconą nazwę podaną w błędzie linkera. (np. draw@graphics@XYZ).
  2. zrzuca wyeksportowane symbole z biblioteki do pliku tekstowego.
  3. wyszukaj wyeksportowany symbol zainteresowania i zauważ, że zniekształcona nazwa jest inna.
  4. Zapłać uwaga na to, dlaczego zniekształcone nazwy kończyły się inaczej. Możesz zobaczyć, że typy parametrów są różne, mimo że wyglądają tak samo w kodzie źródłowym.
  5. powód, dla którego są różne. W powyższym przykładzie różnią się one ze względu na różne pliki nagłówkowe.

[1] przez projekt Mam na myśli zbiór plików źródłowych, które są połączone ze sobą w celu wytworzenia biblioteki lub pliku wykonywalnego.

Edycja 1: przepisanie pierwszej sekcji, aby było łatwiejsze Rozumiem. Proszę skomentuj poniżej, aby dać mi znać, jeśli coś innego wymaga naprawy. Dzięki!

 11
Author: fafaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-12 00:10:26

Brak "extern" w const deklaracjach zmiennych/definicjach (tylko C++)

Dla osób wywodzących się z C może być zaskoczeniem, że w C++ zmienne globalne constmają wewnętrzne (lub statyczne) powiązania. W C tak nie było, ponieważ wszystkie zmienne globalne są domyślnie extern (tzn. gdy brakuje słowa kluczowego static).

Przykład:

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

Poprawne byłoby użycie pliku nagłówkowego i umieszczenie go w pliku2.cpp i plik1.cpp

extern const int test;
extern int test2;

Alternatywnie jeden może zadeklarować zmienną const w pliku1.cpp with explicit extern

 7
Author: Andreas H.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-03 08:01:49

Mimo że jest to dość stare pytanie z wieloma akceptowanymi odpowiedziami, chciałbym podzielić się, jak rozwiązać niejasne "undefined reference to" błąd.

Różne wersje bibliotek

Używałem aliasu, aby odnieść się do std::filesystem::path: system plików jest w bibliotece standardowej od C++17, Ale mój program musiał skompilować również w C++14 więc zdecydowałem się użyć zmiennej alias:

#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

Powiedzmy, że mam trzy pliki: main.cpp, plik.h, plik.cpp:

  • plik.h # include ' s experimental:: filestystem> i zawiera powyższy kod
  • plik.cpp , implementacja pliku.H, #include ' s "file.h "
  • main.cpp # include ' s filestystem> and " file.h "

Zwróć uwagę na różne biblioteki używane w main.cpp i plik.h.od main.cpp #include ' D "file.h " po filestystem >, wersja używanym tam systemem plików był C++17 . Kompilowałem program za pomocą następujących poleceń:

$ g++ -g -std=c++17 -c main.cpp -> kompiluje main.cpp do centrali.o
$ g++ -g -std=c++17 -c file.cpp - > kompiluje plik.cpp i plik.h do pliku.o
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs - > links main.o i teczka.o

W ten sposób Dowolna funkcja zawarta w pliku.o i używane w main.o że wymagane path_t podał błędy "undefined reference" ponieważ main.o std::filesystem::path ale plik.o to std::experimental::filesystem::path.

Rozdzielczość

Aby to naprawić, po prostu musiałem zmienić <:filestystem> w pliku.h do .

 2
Author: Stypox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-27 18:12:32

Podczas łączenia z bibliotekami współdzielonymi upewnij się, że użyte symbole nie są ukryte.

Domyślnym zachowaniem gcc jest to, że wszystkie symbole są widoczne. Jednakże, gdy jednostki translacyjne są budowane z opcją -fvisibility=hidden, tylko funkcje/symbole oznaczone __attribute__ ((visibility ("default"))) są zewnętrzne w wynikowym obiekcie współdzielonym.

Możesz sprawdzić, czy symbole, których szukasz, są zewnętrzne, wywołując:

# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL 

Ukryte/lokalne symbole są wyświetlane przez nm z małymi literami typ symbolu, na przykład t zamiast `T dla sekcji kodu:

nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL

Możesz również użyć nm z opcją -C do demangle nazw (jeśli użyto C++).

Podobnie jak Windows-DLL, można oznaczyć funkcje publiczne za pomocą define, na przykład DLL_PUBLIC zdefiniowane jako:

#define DLL_PUBLIC __attribute__ ((visibility ("default")))

DLL_PUBLIC int my_public_function(){
  ...
}

Co w przybliżeniu odpowiada wersji Windows ' / MSVC:

#ifdef BUILDING_DLL
    #define DLL_PUBLIC __declspec(dllexport) 
#else
    #define DLL_PUBLIC __declspec(dllimport) 
#endif

Więcej informacji o widoczności można znaleźć na wiki gcc.


Gdy jednostka tłumaczeniowa jest kompilowany z -fvisibility=hidden wynikowe symbole mają jeszcze zewnętrzne powiązanie (pokazane z wielkimi literami typ symbolu przez nm) i mogą być używane do zewnętrznego powiązania bez problemu, jeśli pliki obiektowe staną się częścią bibliotek statycznych. Połączenie staje się lokalne tylko wtedy, gdy pliki obiektowe są połączone ze współdzieloną biblioteką.

Aby sprawdzić, które symbole w pliku obiektowym są ukryte, Uruchom:

>>> objdump -t XXXX.o | grep hidden
0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2
 1
Author: ead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-10 08:18:38