Pertanyaan Apakah yang dimaksud dengan referensi tidak langsung / kesalahan simbol eksternal yang belum terselesaikan dan bagaimana cara memperbaikinya?


Apa referensi tidak pasti / kesalahan simbol eksternal yang belum terselesaikan? Apa penyebab umum dan cara memperbaiki / mencegahnya?

Jangan sungkan untuk mengedit / menambahkan milik Anda sendiri.


1194
2017-09-24 22:27


asal


Jawaban:


Menyusun program C ++ berlangsung dalam beberapa langkah, seperti yang ditentukan oleh 2.2  (kredit ke Keith Thompson untuk referensi):

Diutamakan di antara aturan-aturan sintaks terjemahan ditentukan oleh fase-fase berikut [lihat catatan kaki].

  1. Karakter file sumber fisik dipetakan, dengan cara yang ditentukan oleh implementasi, ke kumpulan karakter sumber dasar   (memperkenalkan karakter baris baru untuk indikator end-of-line) jika   perlu. [MENGGUNTING]
  2. Setiap instance dari karakter backslash (\) segera diikuti oleh karakter baris baru dihapus, splicing garis sumber fisik ke   membentuk garis sumber yang logis. [MENGGUNTING]
  3. File sumber didekomposisi menjadi token preprocessing (2.5) dan urutan karakter spasi-putih (termasuk komentar). [MENGGUNTING]
  4. Arahan preprocessing dieksekusi, invokasi makro diperluas, dan _Pragma unary operator expressions dieksekusi. [MENGGUNTING]
  5. Setiap set karakter sumber anggota dalam literal karakter atau string literal, serta setiap urutan pelarian dan universal-karakter-nama   dalam karakter literal atau string literal non-baku, diubah menjadi   anggota yang sesuai dari set karakter eksekusi; [MENGGUNTING]
  6. Token literal string bersebelahan dirangkai.
  7. Karakter spasi putih memisahkan token tidak lagi signifikan. Setiap token preprocessing diubah menjadi token. (2.7). Itu   token yang dihasilkan secara sintaksis dan semantis dianalisis dan   diterjemahkan sebagai unit terjemahan. [MENGGUNTING]
  8. Unit terjemahan yang diterjemahkan dan unit instantiasi digabungkan sebagai berikut: [MENGGUNTING]
  9. Semua referensi entitas eksternal diselesaikan. Komponen pustaka terhubung untuk memenuhi referensi eksternal terhadap entitas yang tidak didefinisikan dalam   terjemahan saat ini. Semua hasil penerjemah tersebut dikumpulkan ke dalam   gambar program yang berisi informasi yang diperlukan untuk eksekusi dalam   lingkungan eksekusi. (Menekankan saya)

[catatan kaki] Implementasi harus berperilaku seolah-olah fase terpisah ini terjadi, meskipun dalam prakteknya fase yang berbeda mungkin dilipat bersama.

Kesalahan yang ditentukan terjadi selama tahap kompilasi terakhir ini, paling sering disebut sebagai penautan. Ini pada dasarnya berarti Anda mengkompilasi banyak file implementasi ke dalam file objek atau pustaka dan sekarang Anda ingin membuatnya bekerja bersama.

Katakanlah Anda mendefinisikan simbol a di a.cpp. Sekarang, b.cpp  dideklarasikan simbol itu dan menggunakannya. Sebelum menghubungkan, itu hanya mengasumsikan bahwa simbol itu didefinisikan suatu tempat, tetapi tidak peduli di mana. Fase yang menghubungkan bertanggung jawab untuk menemukan simbol dan menghubungkannya dengan benar b.cpp (yah, sebenarnya ke objek atau pustaka yang menggunakannya).

Jika Anda menggunakan Microsoft Visual Studio, Anda akan melihat bahwa proyek menghasilkan .lib file. Ini berisi tabel simbol yang diekspor, dan tabel simbol yang diimpor. Simbol yang diimpor dipecahkan terhadap pustaka yang Anda tautkan, dan simbol yang diekspor disediakan untuk pustaka yang menggunakannya .lib (jika ada).

Mekanisme serupa ada untuk kompiler / platform lain.

Pesan kesalahan umum adalah error LNK2001, error LNK1120, error LNK2019 untuk Microsoft Visual Studio dan undefined reference to  symbolName untuk GCC.

Kode:

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

akan menghasilkan kesalahan berikut dengan 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

dan kesalahan serupa dengan 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

Penyebab umum meliputi:


693
2017-09-24 22:27



Anggota kelas:

Yang murni virtual destruktor membutuhkan implementasi.

Mendeklarasikan murni destruktor masih mengharuskan Anda untuk mendefinisikannya (tidak seperti fungsi biasa):

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

Hal ini terjadi karena destructors kelas dasar dipanggil ketika objek dihancurkan secara implisit, jadi definisi diperlukan.

virtual metode harus diimplementasikan atau didefinisikan sebagai murni.

Ini mirip dengan non-virtual metode tanpa definisi, dengan alasan tambahan itu deklarasi murni menghasilkan vtable dummy dan Anda mungkin mendapatkan kesalahan linker tanpa menggunakan fungsi:

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

Agar ini berfungsi, deklarasikan X::foo() sebagai murni:

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

Non-virtual anggota kelas

Beberapa anggota perlu didefinisikan bahkan jika tidak digunakan secara eksplisit:

struct A
{ 
    ~A();
};

Berikut ini akan menghasilkan kesalahan:

A a;      //destructor undefined

Implementasinya bisa inline, dalam definisi kelas itu sendiri:

struct A
{ 
    ~A() {}
};

atau di luar:

A::~A() {}

Jika penerapannya di luar definisi kelas, tetapi di header, metode harus ditandai sebagai inline untuk mencegah definisi ganda.

Semua metode anggota yang digunakan perlu ditentukan jika digunakan.

Kesalahan umum adalah lupa untuk memenuhi syarat nama:

struct A
{
   void foo();
};

void foo() {}

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

Definisi seharusnya

void A::foo() {}

static anggota data harus didefinisikan di luar kelas dalam a unit terjemahan tunggal:

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

Penginisialisasi dapat disediakan untuk a static  const anggota data tipe integral atau enumerasi dalam definisi kelas; Namun, odr-use dari anggota ini masih akan membutuhkan definisi ruang lingkup namespace seperti yang dijelaskan di atas. C ++ 11 memungkinkan inisialisasi di dalam kelas untuk semua static constanggota data.


148
2017-09-24 23:38



Gagal menautkan file pustaka / objek yang sesuai atau menyusun file implementasi

Umumnya, setiap unit terjemahan akan menghasilkan file objek yang berisi definisi simbol yang didefinisikan dalam unit terjemahan itu. Untuk menggunakan simbol-simbol itu, Anda harus menautkan ke file objek tersebut.

Dibawah gcc Anda akan menentukan semua file objek yang akan dihubungkan bersama di baris perintah, atau kompilasi file implementasi bersama.

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

Itu libraryName di sini hanya nama perpustakaan, tanpa tambahan platform khusus. Jadi mis. pada file pustaka Linux biasanya disebut libfoo.so tetapi Anda hanya menulis -lfoo. Pada Windows file yang sama dapat dipanggil foo.lib, tetapi Anda akan menggunakan argumen yang sama. Anda mungkin harus menambahkan direktori tempat file-file itu dapat ditemukan -L‹directory›. Pastikan untuk tidak menulis spasi sesudahnya -l atau -L.

Untuk Xcode: Tambahkan Jalur Pencarian Header Pengguna -> tambahkan Path Pencarian Perpustakaan -> seret dan lepas referensi perpustakaan yang sebenarnya ke dalam folder proyek.

Dibawah MSVS, file ditambahkan ke proyek secara otomatis memiliki file objek mereka dihubungkan bersama dan lib file akan dihasilkan (dalam penggunaan umum). Untuk menggunakan simbol dalam proyek terpisah, Anda akan perlu memasukkan lib file dalam pengaturan proyek. Ini dilakukan di bagian Linker dari properti proyek, di Input -> Additional Dependencies. (jalan menuju lib file seharusnya ditambahkan Linker -> General -> Additional Library Directories) Saat menggunakan pustaka pihak ketiga yang dilengkapi dengan lib file, kegagalan untuk melakukannya biasanya menghasilkan kesalahan.

Bisa juga terjadi bahwa Anda lupa untuk menambahkan file ke kompilasi, dalam hal mana file objek tidak akan dihasilkan. Di gcc Anda akan menambahkan file ke baris perintah. Di MSVS menambahkan file ke proyek akan membuatnya mengkompilasinya secara otomatis (meskipun file dapat, secara manual, dikecualikan secara individual dari build).

Dalam pemrograman Windows, tanda-tanda-tanda bahwa Anda tidak menghubungkan perpustakaan yang diperlukan adalah bahwa nama simbol yang belum terselesaikan dimulai dengan __imp_. Cari nama fungsi dalam dokumentasi, dan harus mengatakan perpustakaan mana yang perlu Anda gunakan. Misalnya, MSDN menempatkan informasi dalam kotak di bagian bawah setiap fungsi di bagian yang disebut "Perpustakaan".


97
2017-09-24 23:37



Dideklarasikan tetapi tidak mendefinisikan variabel atau fungsi.

Deklarasi variabel yang khas adalah

extern int x;

Karena ini hanya sebuah deklarasi, a definisi tunggal diperlukan. Definisi yang sesuai adalah:

int x;

Sebagai contoh, berikut ini akan menghasilkan kesalahan:

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

Pernyataan serupa berlaku untuk fungsi. Mendeklarasikan fungsi tanpa mendefinisikannya menyebabkan kesalahan:

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

Hati-hati bahwa fungsi yang Anda laksanakan benar-benar cocok dengan yang Anda nyatakan. Misalnya, Anda mungkin memiliki kualifikasi-cv yang tidak serasi:

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)

Contoh ketidakcocokan lainnya termasuk

  • Fungsi / variabel dideklarasikan dalam satu ruang nama, didefinisikan di ruang nama lain.
  • Fungsi / variabel dinyatakan sebagai anggota kelas, didefinisikan sebagai global (atau sebaliknya).
  • Tipe kembalian fungsi, nomor parameter dan jenis, dan konvensi pemanggilan tidak semuanya tepat.

Pesan kesalahan dari kompilator sering akan memberi Anda deklarasi lengkap dari variabel atau fungsi yang dideklarasikan tetapi tidak pernah didefinisikan. Bandingkan dengan erat dengan definisi yang Anda berikan. Pastikan setiap detail cocok.


91
2017-09-24 23:38



Urutan di mana librari terkait interdependen ditentukan adalah salah.

Urutan di mana pustaka terhubung TIDAK masalah jika pustaka bergantung satu sama lain. Secara umum, jika perpustakaan A tergantung perpustakaan B, kemudian libA  HARUS muncul sebelumnya libB di bendera penaut.

Sebagai contoh:

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

Buat pustaka:

$ 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

Menyusun:

$ 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

Jadi untuk mengulangi lagi, pesanan TIDAK masalah!


73
2017-07-10 11:46



apa yang dimaksud dengan "referensi tidak terpantul / simbol eksternal yang belum terselesaikan"

Saya akan mencoba menjelaskan apa yang dimaksud dengan "rujukan tidak terukur / simbol eksternal yang belum terselesaikan".

catatan: saya menggunakan g ++ dan Linux dan semua contoh untuk itu

Misalnya kami memiliki beberapa kode

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

dan

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

Buat file objek

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

Setelah fase assembler, kami memiliki file objek, yang berisi simbol apa pun untuk diekspor. Lihatlah simbolnya

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

Saya telah menolak beberapa baris dari output, karena itu tidak masalah

Jadi, kita melihat simbol-simbol berikut untuk diekspor.

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

src2.cpp tidak mengekspor apa pun dan kami tidak melihat simbolnya

Tautkan file objek kami

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

dan jalankan

$ ./prog
123

Linker melihat simbol yang diekspor dan menautkannya. Sekarang kami mencoba menghapus tanda komentar di src2.cpp seperti di sini

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

dan membangun kembali file objek

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

OK (tidak ada kesalahan), karena kami hanya membuat file objek, penautan belum selesai. Coba tautkan

$ 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

Ini terjadi karena local_var_name kami statis, yaitu tidak terlihat untuk modul lain. Sekarang lebih dalam. Dapatkan hasil fase terjemahan

$ 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

Jadi, kami telah melihat tidak ada label untuk local_var_name, itu sebabnya tautan tidak menemukannya. Tapi kami adalah peretas :) dan kami dapat memperbaikinya. Buka src1.s di editor teks Anda dan ubah

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

untuk

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

Anda harus seperti di bawah ini

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

kami telah mengubah visibilitas local_var_name dan menetapkan nilainya ke 456789. Cobalah untuk membangun file objek dari itu

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

ok, lihat hasil keluaran (simbol)

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

sekarang local_var_name telah mengikat GLOBAL (adalah LOKAL)

link

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

dan jalankan

$ ./prog 
123456789

ok, kami meretasnya :)

Jadi, sebagai hasilnya - sebuah "kesalahan referensi tidak terpecahkan / simbol eksternal yang belum terselesaikan" terjadi ketika penaut tidak dapat menemukan simbol global dalam file objek.


63
2017-09-24 23:39



Simbol didefinisikan dalam program C dan digunakan dalam kode C ++.

Fungsi (atau variabel) void foo() didefinisikan dalam program C dan Anda mencoba untuk menggunakannya dalam program C ++:

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

Penghubung C ++ mengharapkan nama yang akan dibuatkan, jadi Anda harus menyatakan fungsi sebagai:

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

Secara ekuivalen, alih-alih didefinisikan dalam program C, fungsi (atau variabel) void foo() didefinisikan dalam C ++ tetapi dengan hubungan C:

extern "C" void foo();

dan Anda mencoba menggunakannya dalam program C ++ dengan tautan C ++.

Jika seluruh perpustakaan termasuk dalam file header (dan dikompilasi sebagai kode C); masukkan harus sebagai berikut;

extern "C" {
    #include "cheader.h"
}

60
2017-12-03 18:11