未定義の参照/未解決の外部シンボルエラーとは何ですか?一般的な原因は何ですか?またそれらを修正または防止する方法は?
自分で編集/追加してください。
C++プログラムのコンパイルは、 2.2 で指定されているように、いくつかのステップで行われます(参考のためにKeith Thompsonにクレジット) :
翻訳の構文規則の中での優先順位は、次のフェーズ[脚注]によって指定されます。
- 物理ソースファイルの文字は、必要に応じて、実装定義の方法で、基本ソース文字セット(行末インジケータ用の改行文字の導入)にマッピングされます。 [SNIP]
- 直後に改行文字が続くバックスラッシュ文字(\)の各インスタンスは削除され、物理ソース行をに結合して論理ソース行を形成します。 [SNIP]
- ソースファイルは、前処理トークン(2.5)と一連の空白文字(コメントを含む)に分解されます。 [SNIP]
- 前処理指令が実行され、マクロ呼び出しが拡張され、_Pragma単項演算子式が実行されます。 [SNIP]
- 文字リテラルまたはストリング・リテラルの各ソース文字セット・メンバー、および文字リテラルまたは非ロー・ストリング・リテラルの各エスケープ・シーケンスおよびユニバーサル文字名は、に変換されます。実行文字セットの対応するメンバ[SNIP]
- 隣接する文字列リテラルトークンは連結されています。
- トークンを区切る空白文字は重要ではなくなりました。各前処理トークンはトークンに変換されます。 (2.7)結果として生じるトークンは、構文的および意味的に分析され、翻訳単位として翻訳されます。 [SNIP]
- 翻訳された翻訳単位と具体化単位は次のように結合されます。[SNIP]
- すべての外部エンティティ参照は解決されます。図書館の構成要素は、現在の訳文で定義されていない実体への外部参照を満たすためにリンクされています。このようなトランスレータの出力はすべて、その実行環境での実行に必要な情報を含むプログラムイメージにまとめられます。 (私の強調)
[footnote]実装はあたかもこれらの別々のフェーズが発生するかのように振る舞わなければなりませんが、実際には異なるフェーズが一緒に折り畳まれるかもしれません。
指定されたエラーは、この最後のコンパイル段階で発生します。最も一般的にはリンクと呼ばれます。これは基本的に、一連の実装ファイルをオブジェクトファイルまたはライブラリにコンパイルした後、それらを連携させたいということです。
a.cpp
にシンボルa
を定義したとします。さて、b.cpp
declaratedそのシンボルとそれを使った。リンクする前に、そのシンボルがsomewhereで定義されていると単純に仮定しますが、それはまだどこでも構いません。リンクフェーズは、シンボルを見つけて、それをb.cpp
(実際にはそれを使用するオブジェクトまたはライブラリ)に正しくリンクする責任があります。
Microsoft Visual Studioを使用している場合は、プロジェクトによって.lib
ファイルが生成されます。これらはエクスポートされたシンボルのテーブルとインポートされたシンボルのテーブルを含みます。インポートされたシンボルはリンクしているライブラリに対して解決され、エクスポートされたシンボルはその.lib
を使用するライブラリに対して提供されます(もしあれば)。
他のコンパイラ/プラットフォームにも同様のメカニズムがあります。
一般的なエラーメッセージは、error LNK2001
、error LNK1120
、 Microsoft Visual Studio のerror LNK2019
、および _ gcc _ のundefined reference to
symbolNameです。
コード:
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;
}
_ 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
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
一般的な原因は次のとおりです。
#pragma
を使用するときに誤って.lib拡張子を入力するか含めない(Microsoft Visual Studio)UNICODE
の定義が矛盾していますvirtual
デストラクタは実装が必要です。純粋なデストラクタを宣言するには、(通常の関数とは異なり)それを定義する必要があります。
struct X
{
virtual ~X() = 0;
};
struct Y : X
{
~Y() {}
};
int main()
{
Y y;
}
//X::~X(){} //uncomment this line for successful definition
これは、オブジェクトが暗黙的に破棄されるときに基本クラスのデストラクタが呼び出されるために定義が必要になるために発生します。
virtual
メソッドは、実装するか、純粋なものとして定義する必要があります。純粋な宣言はダミーのvtableを生成し、関数を使用せずにリンカエラーが発生する可能性があるという追加の理由付きで、これは定義なしの非virtual
メソッドに似ています。
struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
int main()
{
Y y; //linker error although there was no call to X::foo
}
これが機能するためには、X::foo()
をpureとして宣言してください。
struct X
{
virtual void foo() = 0;
};
virtual
以外のクラスメンバー明示的に使用されていなくても、一部のメンバーを定義する必要があります。
struct A
{
~A();
};
次のようにするとエラーが発生します。
A a; //destructor undefined
実装は、クラス定義自体の中でインラインにすることができます。
struct A
{
~A() {}
};
または外:
A::~A() {}
実装がクラス定義の外側、ヘッダー内にある場合は、多重定義を防ぐためにメソッドをinline
としてマークする必要があります。
使用されている場合は、使用されているすべてのメンバーメソッドを定義する必要があります。
struct A
{
void foo();
};
void foo() {}
int main()
{
A a;
a.foo();
}
定義は
void A::foo() {}
static
データメンバは 単一の翻訳単位でクラスの外側で定義されなければなりません :struct X
{
static int x;
};
int main()
{
int x = X::x;
}
//int X::x; //uncomment this line to define X::x
クラス定義内の整数型または列挙型のstatic
const
データメンバには、初期化子を指定できます。ただし、このメンバーの使用には、上記のとおり名前空間スコープの定義が必要です。 C++ 11では、すべてのstatic const
データメンバーのクラス内での初期化が許可されています。
通常、各翻訳単位は、その翻訳単位で定義されているシンボルの定義を含むオブジェクトファイルを生成します。 これらのシンボルを使用するには、それらのオブジェクトファイルに対してリンクする必要があります。
gcc の下では、コマンドラインで一緒にリンクされるすべてのオブジェクトファイルを指定するか、または実装ファイルをまとめてコンパイルします。
g++ -o test objectFile1.o objectFile2.o -lLibraryName
ここでのlibraryName
は、プラットフォーム固有の追加なしで、単にライブラリの裸の名前です。だから例えばLinuxでは、ライブラリファイルは通常libfoo.so
と呼ばれますが、-lfoo
を書くだけです。 Windowsでは同じファイルがfoo.lib
と呼ばれるかもしれませんが、同じ引数を使用します。 -L‹directory›
を使ってそれらのファイルが見つかるディレクトリを追加する必要があるかもしれません。 -l
または-L
の後にスペースを入れないでください。
XCode :ユーザヘッダ検索パスを追加 - >ライブラリ検索パスを追加 - >実際のライブラリ参照をプロジェクトフォルダにドラッグアンドドロップします。
_ msvs _ の下では、プロジェクトに追加されたファイルは自動的にそれらのオブジェクトファイルを相互にリンクさせ、lib
ファイルが生成されます(一般的な使用法で)。別のプロジェクトでシンボルを使用するには、プロジェクト設定にlib
ファイルを含める必要があります。これはプロジェクトプロパティのリンカセクションのInput -> Additional Dependencies
で行われます。 (lib
ファイルへのパスはLinker -> General -> Additional Library Directories
にを追加する必要があります)lib
ファイルで提供されているサードパーティ製のライブラリを使用する場合、通常、そうしないとエラーが発生します。
ファイルをコンパイルに追加するのを忘れてしまうこともあります。その場合、オブジェクトファイルは生成されません。 gcc では、ファイルをコマンドラインに追加します。 _ msvs _ でプロジェクトにファイルを追加すると自動的にコンパイルされます(手動でも、個別にビルドから除外することもできます)。
Windowsプログラミングでは、必要なライブラリをリンクしていないという事実を示す記号は、未解決のシンボルの名前が__imp_
で始まるということです。ドキュメントで関数の名前を調べれば、どのライブラリを使う必要があるかがわかります。たとえば、MSDNは "Library"というセクションの各関数の下部にあるボックスに情報を配置します。
典型的な変数宣言は
extern int x;
これは宣言にすぎないので、 単一の定義 が必要です。対応する定義は次のようになります。
int x;
たとえば、次のようにするとエラーが発生します。
extern int x;
int main()
{
x = 0;
}
//int x; // uncomment this line for successful definition
同様のことが関数にも当てはまります。定義せずに関数を宣言するとエラーになります。
void foo(); // declaration only
int main()
{
foo();
}
//void foo() {} //uncomment this line for successful definition
実装する関数が宣言した関数と正確に一致するように注意してください。たとえば、cv-qualifiersが一致していないとします。
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)
ミスマッチの他の例としては、
コンパイラからのエラーメッセージは、多くの場合、宣言されたが定義されていない変数または関数の完全な宣言を示します。あなたが提供した定義とよく比較してください。 すべての詳細が一致することを確認してください。
ライブラリが互いに依存している場合、ライブラリがリンクされている順序は重要です。一般に、ライブラリA
がライブラリB
に依存している場合、libA
MUSTはリンカフラグのlibB
の前に現れます。
例えば:
// 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;
};
ライブラリを作成します。
$ 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
コンパイル:
$ 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
もう一度繰り返すと、DOという順番が重要です。
"未定義の参照/未解決の外部シンボル"とは何ですか /
「未定義の参照/未解決の外部シンボル」とは何かを説明しようと思います。
注:私はg + +とLinuxを使用し、すべての例はそれのためです
例えばいくつかのコードがあります
// 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;
}
そして
// 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);
}
オブジェクトファイルを作る
$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o
アセンブラフェーズの後、エクスポートするシンボルを含むオブジェクトファイルができます。 記号を見てください
$ 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]
問題にならないので、出力からいくつかの行を拒否しました。
だから、私たちはエクスポートするシンボルに従ってください。
[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable
src2.cppは何もエクスポートせず、そのシンボルも見たことがない
オブジェクトファイルをリンクする
$ g++ src1.o src2.o -o prog
それを実行
$ ./prog
123
リンカはエクスポートされたシンボルを見てそれをリンクします。さて、ここでsrc2.cppの行のコメントを外します。
// 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);
}
オブジェクトファイルを作成して再構築する
$ g++ -c src2.cpp -o src2.o
OK(エラーなし)、私たちはオブジェクトファイルをビルドするだけなので、リンクはまだ行われていません。
$ 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
これは、local_var_nameが静的であるために発生しました。つまり、他のモジュールからは見えません。 今、もっと深く。翻訳フェーズの出力を取得する
$ 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
だから、私たちはlocal_var_nameのラベルがないのを見ました、それがリンカがそれを見つけられなかった理由です。しかし、我々はハッカーです:)そして我々はそれを修正することができます。テキストエディタでsrc1.sを開き、変更します。
.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
すなわち、あなたは以下のように持っているべきです
.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:
; ...
local_var_nameの可視性を変更し、その値を456789に設定しました。そこからオブジェクトファイルを構築してみてください
$ g++ -c src1.s -o src2.o
わかりました、再表示の出力を参照してください(シンボル)
$ readelf --symbols src1.o
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
local_var_nameはBind GLOBALになりました(以前はLOCALでした)
リンク
$ g++ src1.o src2.o -o prog
それを実行
$ ./prog
123456789
わかりました、私達はそれをハックします:)
そのため、結果として、リンカがオブジェクトファイルでグローバルシンボルを見つけることができないと、「未定義の参照/未解決の外部シンボルエラー」が発生します。
関数(または変数)void foo()
がCプログラムで定義されていて、C++プログラムで使用しようとしています。
void foo();
int main()
{
foo();
}
C++リンカは名前が壊されることを期待しているので、関数を次のように宣言する必要があります。
extern "C" void foo();
int main()
{
foo();
}
同様に、Cプログラムで定義されるのではなく、関数(または変数)void foo()
がC++で定義されていますが、Cリンケージを使用しています。
extern "C" void foo();
そして、あなたはそれをC++リンケージを持つC++プログラムで使おうとします。
ライブラリ全体がヘッダーファイルに含まれている(そしてCコードとしてコンパイルされている)場合。インクルードは次のようになります。
extern "C" {
#include "cheader.h"
}
それ以外のすべてが失敗した場合は、再コンパイルしてください。
私は最近、問題のあるファイルを再コンパイルすることで、Visual Studio 2012で未解決の外部エラーを取り除くことができました。私が再構築したとき、エラーは消えました。
これは通常、2つ(またはそれ以上)のライブラリーに循環依存関係があるときに起こります。ライブラリAはB.libのシンボルを使用しようとし、ライブラリBはA.libのシンボルを使用しようとします。どちらからも始めることができません。 Aをコンパイルしようとすると、B.libが見つからないため、リンク手順は失敗します。 A.libが生成されますが、DLLは生成されません。その後、Bをコンパイルします。これは成功し、B.libを生成します。 B.libが発見されたので、Aの再コンパイルは動作します。
MSVSでは、__declspec(dllexport)
および__declspec(dllimport)
を使用してエクスポートおよびインポートするシンボルを指定する必要があります。
この二重の機能は通常、マクロの使用によって得られます。
#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif
マクロTHIS_MODULE
は、関数をエクスポートするモジュール内でのみ定義されます。そのように、宣言:
DLLIMPEXP void foo();
に展開
__declspec(dllexport) void foo();
現在のモジュールに定義が含まれているため、関数にエクスポートするようコンパイラーに指示します。宣言を別のモジュールに含めると、次のように展開されます。
__declspec(dllimport) void foo();
そして、定義がリンク先のライブラリの1つにあることをコンパイラーに指示します( 1) も参照)。
インポート/エクスポートクラスを同様にすることができます。
class DLLIMPEXP X
{
};
これは、すべてのVC++プログラマーが何度も何度も見てきた最もわかりにくいエラーメッセージの1つです。最初に物事を明確にしましょう。
A.シンボルとは何ですか? つまり、シンボルは名前です。それは、変数名、関数名、クラス名、typedef名、あるいはC++言語に属するそれらの名前とサイン以外の何でもでありえます。これはユーザー定義または依存関係ライブラリーによって導入されたものです(別のユーザー定義)。
B.外部とは何ですか? VC++では、すべてのソースファイル(.cpp、.cなど)が変換単位と見なされ、コンパイラは一度に1つの単位をコンパイルし、そのためのオブジェクトファイル(.obj)を1つ生成します。現在の翻訳単位(このソースファイルに含まれるすべてのヘッダーファイルは前処理され、この翻訳単位の一部と見なされます。)翻訳単位内のすべてのものは内部と見なされ、それ以外のものはすべて外部と見なされます。 C++では、extern
、__declspec (dllimport)
などのキーワードを使用して外部シンボルを参照できます。
C.「解決」とは何ですか? 解決はリンク時の用語です。リンク時には、リンカは、定義を内部的に見つけることができないオブジェクトファイル内のすべてのシンボルの外部定義を見つけようとします。この検索プロセスの範囲は次のとおりです。
この検索プロセスは解決と呼ばれます。
D.最後に、なぜ未解決の外部シンボルなのか? リンカが、内部定義を持たないシンボルの外部定義を見つけられない場合、Unresolved External Symbolエラーを報告します。
E. LNK2019の考えられる原因 :未解決の外部シンボルエラーこのエラーはリンカが外部シンボルの定義を見つけられなかったことが原因であることがすでにわかっています。
たとえば、a.cppにfooという関数が定義されているとします。
int foo()
{
return 0;
}
B.cppでは、関数fooを呼び出したいので、以下を追加します。
void foo();
関数foo()を宣言して別の関数本体で呼び出すには、bar()
とします。
void bar()
{
foo();
}
このコードをビルドすると、fooが未解決のシンボルであることを示すLNK2019エラーが発生します。この場合、foo()の定義はa.cppにありますが、呼び出しているものとは異なります(異なる戻り値)。これは定義が存在する場合です。
ライブラリ内のいくつかの関数を呼び出したいが、インポートライブラリがプロジェクト設定の追加の依存関係リスト(:Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
から設定)に追加されていない場合。現在の検索範囲に定義が存在しないため、リンカはLNK2019をレポートするようになりました。
特殊化されていないテンプレートは、それらを使用するすべての翻訳単位から見えるように定義を持たなければなりません。つまり、テンプレートの定義を実装ファイルに分離することはできません。実装を分離する必要がある場合、通常の回避策はがテンプレートを宣言するヘッダーの最後に含めるimpl
ファイルを用意することです。一般的な状況は次のとおりです。
template<class T>
struct X
{
void foo();
};
int main()
{
X<int> x;
x.foo();
}
//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}
これを修正するには、X::foo
の定義をヘッダーファイルまたはそれを使用する翻訳単位から見える場所に移動する必要があります。
特殊化されたテンプレートは実装ファイルに実装することができ、実装は可視である必要はありませんが、特殊化は事前に宣言されている必要があります。
さらなる説明と他の可能な解決策(明示的な具体化)については この質問と回答を参照してください 。
未定義のWinMain@16
または類似の'unusual'main()
エントリポイントの参照 (特に visual-studio の場合)。
実際のIDEでは正しいプロジェクトタイプを選択し損なっているかもしれません。 IDEはバインドしたい場合があります。 Windowsアプリケーションは、一般的に使用されているint main(int argc, char** argv);
シグネチャの代わりに、そのようなエントリポイント関数(上記の欠落している参照で指定されている)に射影します。
IDEがPlain Console Projectsをサポートしている場合は、Windowsアプリケーションプロジェクトではなく、このプロジェクトタイプを選択することをお勧めします。
また、サードパーティのライブラリを使用している場合は、正しい32/64ビットバイナリがあることを確認してください。
マイクロソフトは、リンク時に正しいライブラリを参照するための#pragma
を提供しています。
#pragma comment(lib, "libname.lib")
ライブラリのディレクトリを含むライブラリパスに加えて、これはライブラリのフルネームでなければなりません。
Visual Studio NuGetパッケージを新しいツールセットバージョン用に更新する必要があります
LibpngをVisual Studio 2013とリンクしようとしたところ、この問題が発生しました。問題は、パッケージファイルにVisual Studio 2010および2012用のライブラリしかないことです。
正しい解決策は、開発者が更新されたパッケージをリリースしてからアップグレードすることを望みますが、VS2012ライブラリファイルを指定して、VS2013用の追加設定をハッキングすることによって私のために働きました。
私はpackagename\build\native\packagename.targets
とそのファイルの中を見つけて、すべてのv110
セクションをコピーすることによって(ソリューションのディレクトリの中のpackages
フォルダーにある)パッケージを編集しました。 条件フィールドのみ v110
をv120
に変更しました - /ファイル名のパスをすべてv110
のままにするように注意してください。これにより、Visual Studio 2013から2012年のライブラリへのリンクが許可され、この場合は正常に機能しました。
C++で書かれた大きなプロジェクトがあり、そこには何千もの.cppファイルと何千もの.hファイルがあるとします。そして、このプロジェクトも10個の静的ライブラリに依存しているとしましょう。 Windows上にいて、プロジェクトをVisual Studio 20xxでビルドしているとしましょう。 Ctrl + F7 Visual Studioを押してソリューション全体のコンパイルを開始したとき(ソリューションにプロジェクトが1つだけあるとします)
コンパイルの意味は何ですか?
2番目のコンパイルステップは、Linkerによって行われます。Linkerは、すべてのオブジェクトファイルをマージし、最後に(実行可能ファイルまたはライブラリの可能性がある)出力をビルドします。
プロジェクトをリンクする手順
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
のように聞こえます。観測
この種のエラーを解決する方法
コンパイラ時間エラー:
リンカ時間エラー
#pragma once
を使用します。私は最近この問題を抱えていました、そしてそれは結局のところ それはVisual Studio Express 2013のバグでした 。私はプロジェクトからソースファイルを削除し、バグを克服するためにそれを再追加しなければなりませんでした。
コンパイラ/ IDEのバグであると思われる場合に試す手順:
最近のほとんどのリンカーには、さまざまな程度に出力される詳細オプションが含まれています。
Gccとclangの場合通常はコマンドラインに-v -Wl,--verbose
または-v -Wl,-v
を追加します。詳細はこちらをご覧ください。
MSVCの場合、/VERBOSE
(特に/VERBOSE:LIB
)がリンクコマンドラインに追加されます。
/VERBOSE
リンカオプション 。Linked .libファイルは.dll に関連付けられています
私は同じ問題を抱えていました。 MyProjectとTestProjectというプロジェクトがあるとします。 MyProjectのlibファイルをTestProjectに効果的にリンクしました。ただし、このlibファイルはMyProjectのDLLがビルドされたときに作成されました。また、MyProjectのすべてのメソッドのソースコードを含めるのではなく、DLLのエントリポイントにアクセスするだけでした。
この問題を解決するために、MyProjectをLIBとして作成し、TestProjectをこの.libファイルにリンクしました(生成された.libファイルをTestProjectフォルダにコピーアンドペーストします)。 MyProjectをDLLとして再構築することができます。 TestProjectがリンクされているlibには、MyProjectのクラス内のすべてのメソッドのコードが含まれているのでコンパイルされます。
リンカエラーに関しては、人々がこの質問に向けられているように思われるので、これをここに追加します。
GCC 5.2.0でのリンカエラーの考えられる原因の1つは、新しいlibstdc ++ライブラリABIがデフォルトで選択されていることです。
Std :: __ cxx11名前空間またはタグ[abi:cxx11]の型を含むシンボルへの未定義の参照についてリンカエラーが発生した場合は、おそらく_GLIBCXX_USE_CXX11_ABIの異なる値でコンパイルされたオブジェクトファイルをリンクしようとしていることを示していますマクロ。これは通常、古いバージョンのGCCでコンパイルされたサードパーティのライブラリにリンクしているときに発生します。サードパーティのライブラリを新しいABIで再構築できない場合は、コードを古いABIで再コンパイルする必要があります。
そのため、5.1.0以降にGCCに切り替えたときに突然リンカエラーが発生した場合は、これをチェックする必要があります。
リンカスクリプトをサポートしないGNU ldのラッパー
いくつかの.soファイルは実際には GNU ldリンカスクリプト です。 libtbb.so fileは、次の内容を持つASCIIテキストファイルです。
INPUT (libtbb.so.2)
いくつかのより複雑なビルドはこれをサポートしないかもしれません。たとえば、コンパイラオプションに-vを含めると、 mainwin gcc wrapper mwdip はリンクするライブラリの詳細出力リストにあるリンカスクリプトコマンドファイルを破棄します。代わりに、リンカスクリプト入力コマンドファイルをファイルのコピー(またはシンボリックリンク)に置き換えます。
cp libtbb.so.2 libtbb.so
あるいは、-l引数を.soのフルパスに置き換えることもできます。 -ltbb
の代わりに/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
を実行してください。
libfoo
がlibbar
に依存している場合、リンケージはlibfoo
の前にlibbar
を正しく配置します。undefined reference to
somethingエラーで失敗します。#include
dを持つヘッダーファイルで宣言され、実際にリンクしているライブラリで定義されています。例はCにあります。C++でも同様に使用できます。
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;
}
静的ライブラリを構築します。
$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o
プログラムをコンパイルします。
$ gcc -I. -c -o eg1.o eg1.c
libmy_lib.a
とリンクしようとして失敗します:
$ 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
次のように、1ステップでコンパイルしてリンクした場合も同じ結果になります。
$ 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
libz
を含む最小限の例eg2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%s\n",zlibVersion());
return 0;
}
プログラムをコンパイルします。
$ gcc -c -o eg2.o eg2.c
プログラムをlibz
とリンクしようとして失敗します。
$ 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
一度にコンパイルしてリンクする場合も同じです:
$ 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
pkg-config
を含む例2のバリエーション:
$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
プログラムを作成するためにリンクするオブジェクトファイルとライブラリのシーケンスでは、それらを参照するオブジェクトファイルの前にライブラリを配置しています。ライブラリを配置する必要がありますafterライブラリを参照するオブジェクトファイル。
例1を正しくリンクします。
$ gcc -o eg1 eg1.o -L. -lmy_lib
成功:
$ ./eg1
Hello World
例2を正しくリンクします。
$ gcc -o eg2 eg2.o -lz
成功:
$ ./eg2
1.2.8
例2 pkg-config
バリエーションを正しくリンクします。
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
ここからの読み取りはオプションです。
デフォルトでは、GCCによって生成されたリンケージコマンドは、ディストリビューションで、リンケージ内のファイルをコマンドラインシーケンスで左から右に消費します。ファイルがsomethingを参照しており、その定義が含まれていないことが検出された場合、さらに右側のファイルの定義を検索します。最終的に定義が見つかった場合、参照は解決されます。最後に参照が解決されない場合、リンケージは失敗します。リンカーは逆方向に検索しません。
まず、例1、静的ライブラリmy_lib.a
を使用
静的ライブラリは、オブジェクトファイルのインデックス付きアーカイブです。リンカは、リンケージシーケンスで-lmy_lib
を見つけ、これが静的ライブラリ./libmy_lib.a
を参照していることを把握すると、プログラムがlibmy_lib.a
内のオブジェクトファイルを必要とするかどうかを知ります。
libmy_lib.a
にはオブジェクトファイル、つまりmy_lib.o
のみが存在し、my_lib.o
には関数hw
のみが定義されています。
リンカは、プログラムがhw
を参照していることが既にわかっている場合に、プログラムに既に追加されている1つ以上のオブジェクトファイルでmy_lib.o
が必要であると判断します。追加済みのオブジェクトファイルには、hw
の定義が含まれています。
それが当てはまる場合、リンカーはライブラリからmy_lib.o
のコピーを抽出し、プログラムに追加します。次に、プログラムにはhw
の定義が含まれているため、hw
への参照はresolvedです。
次のようなプログラムをリンクしようとすると:
$ gcc -o eg1 -L. -lmy_lib eg1.o
リンカ追加されていませんeg1.o
プログラムへ-lmy_lib
が表示されたとき。その時点では、eg1.o
を見ていないためです。プログラムはまだhw
への参照を作成していません。作成する参照はすべてeg1.o
にあるため、まだ参照を作成していませんat allです。
そのため、リンカはmy_lib.o
をプログラムに追加せず、libmy_lib.a
を使用しません。
次に、eg1.o
を見つけて、プログラムに追加します。リンケージシーケンスのオブジェクトファイルは、常にプログラムに追加されます。現在、プログラムはhw
への参照を作成しますが、hw
の定義は含まれていません。しかし、リンケージシーケンスには、欠落している定義を提供できるものは何もありません。 hw
への参照がnresolvedになり、リンクが失敗します。
次に、例2、共有ライブラリlibz
を使用
共有ライブラリは、オブジェクトファイルやそれに類するもののアーカイブではありません。 programに似ており、main
関数を持たず、代わりに定義された複数の他のシンボルを公開するため、他のプログラムは実行時にそれらを使用できます。
今日の多くのLinuxディストリビューションは、GCCツールチェーンを構成して、その言語ドライバー(gcc
、g++
、gfortran
など)がシステムリンカー(ld
)に共有ライブラリをリンクするよう指示します。 必要に応じて基礎。これらのディストリビューションの1つがあります。
これは、リンカがリンケージシーケンスで-lz
を検出し、これが共有ライブラリ(たとえば)/usr/lib/x86_64-linux-gnu/libz.so
を参照していることを見つけたときに、プログラムに追加した参照があるかどうかを知りたいことを意味しますまだ定義されていないものには、libz
によってエクスポートされる定義があります
それが当てはまる場合、リンカはnotlibz
からチャンクをコピーしてプログラムに追加します。代わりに、次のようにプログラムのコードを修正するだけです。
実行時に、システムプログラムローダーは、プログラムのコピーを読み込むたびに、libz
のコピーをプログラムと同じプロセスに読み込んで実行します。
実行時に、プログラムがlibz
で定義されたものを参照するときはいつでも、その参照は同じプロセスでlibz
のコピーによってエクスポートされた定義を使用します。
プログラムは、libz
によってエクスポートされた定義を持つもの、つまりeg2.c
で一度だけ参照される関数zlibVersion
のみを参照したいと考えています。リンカーがその参照をプログラムに追加し、libz
によってエクスポートされた定義を見つけた場合、参照はresolved
しかし、次のようなプログラムをリンクしようとすると:
gcc -o eg2 -lz eg2.o
例1とまったく同じように、イベントの順序が間違っています。リンカが-lz
を検出した時点で、プログラム内のあらゆるものへのno参照があります。 eg2.o
、まだ見られていません。そのため、リンカはlibz
を使用しないと判断します。 eg2.o
に到達し、プログラムに追加し、zlibVersion
への未定義の参照がある場合、リンケージシーケンスは終了します。その参照は未解決であり、リンクは失敗します。
最後に、例2のpkg-config
バリエーションには、明確な説明があります。シェル拡張後:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
になる:
gcc -o eg2 -lz eg2.o
これも例2です。
リンケージ:
gcc -o eg2 -lz eg2.o
あなたのためにうまく動作します!
(または、Fedora 23ではそのリンケージは正常に機能しましたが、Ubuntu 16.04では失敗します)
これは、リンケージが機能するディストリビューションが、共有ライブラリをリンクするようにGCCツールチェーンを構成しないディストリビューションの1つであるためですas-needed。
昔、Unixのようなシステムでは、異なるルールで静的ライブラリと共有ライブラリをリンクするのが普通でした。リンケージシーケンス内の静的ライブラリは、例1で説明したas-neededに基づいてリンクされましたが、共有ライブラリは無条件にリンクされました。
この動作は、共有ライブラリがプログラムで必要かどうかをリンカが熟考する必要がないため、リンク時に経済的です。共有ライブラリの場合は、リンクします。そして、ほとんどのリンケージのほとんどのライブラリは共有ライブラリです。しかし、欠点もあります:-
runtimeでは不経済です。共有ライブラリが必要でなくても、プログラムとともに共有ライブラリがロードされる可能性があるためです。
静的ライブラリと共有ライブラリの異なるリンケージルールは、リンケージの-lfoo
が/some/where/libfoo.a
に解決するのか/some/where/libfoo.so
に解決するのかわからない未熟なプログラマーを混乱させる可能性があります。とにかく共有ライブラリと静的ライブラリの違いを理解してください。
このトレードオフが今日の分裂的な状況につながっています。一部のディストリビューションでは、共有ライブラリのGCCリンケージルールが変更され、すべてのライブラリにas-needed原則が適用されます。一部のディストリビューションは古い方法にこだわっています。
私がちょうどするなら:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
確かに、gccは最初にeg1.c
をコンパイルし、次に結果のオブジェクトファイルをlibmy_lib.a
にリンクする必要があります。では、リンクを行っているときにオブジェクトファイルが必要であることをどのように認識できないのでしょうか。
単一のコマンドでコンパイルおよびリンクしても、リンクシーケンスの順序は変わらないためです。
上記のコマンドを実行すると、gcc
により、コンパイル+リンケージが必要であることがわかります。そのため、舞台裏では、コンパイルコマンドを生成して実行し、次にリンケージコマンドを生成して実行します。これはyoが2つのコマンドを実行した場合と同じです。
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
doこれら2つのコマンドを実行した場合と同様に、リンケージは失敗します。失敗で気付く唯一の違いは、gccがコンパイル+リンクの場合にeg1.o
を使用するように指示していないため、一時オブジェクトファイルを生成したことです。私たちは見る:
/tmp/ccQk1tvs.o: In function `main'
の代わりに:
eg1.o: In function `main':
相互依存ライブラリを間違った順序に並べることは、必要定義よりもリンケージで後から来るものの定義提供であるファイルを取得できる1つの方法にすぎません。ライブラリを参照するオブジェクトファイルの前にライブラリを置くことも、同じ間違いをする別の方法です。
フレンド演算子(または関数)を持つテンプレートタイプのコードスニペットを考えます。
template <typename T>
class Foo {
friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};
operator<<
は非テンプレート関数として宣言されています。 T
とともに使用されるすべての型Foo
には、テンプレート化されていないoperator<<
が必要です。たとえば、宣言された型Foo<int>
がある場合、次のような演算子実装が必要です。
std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
実装されていないため、リンカはそれを見つけられず、エラーになります。
これを修正するには、Foo
型の前にテンプレート演算子を宣言してから、適切なインスタンス化である友人として宣言することができます。構文は少し厄介ですが、次のようになります。
// 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
}
上記のコードは、演算子の友情を対応するFoo
のインスタンス化に限定します。つまり、operator<< <int>
インスタンス化は、Foo<int>
のインスタンス化の非公開メンバーにアクセスすることに制限されます。
代替案があります。
次のように、友情をテンプレートのすべてのインスタンス化に拡張することを許可します。
template <typename T>
class Foo {
template <typename T1>
friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
// ...
};
あるいは、operator<<
の実装はクラス定義内でインラインで行うことができます。
template <typename T>
class Foo {
friend std::ostream& operator<<(std::ostream& os, const Foo& a)
{ /*...*/ }
// ...
};
注 、演算子(または関数)の宣言のみがクラスにある場合、 cppreference から、名前は「通常の」検索では使用できず、引数依存の検索でのみ使用できます。
クラスまたはクラステンプレートX内のフレンド宣言で最初に宣言された名前は、Xを囲む最も内側のネームスペースのメンバーになりますが、ネームスペーススコープでの一致する宣言が提供された...
cppreference と C++ FAQ で、テンプレートの友達についてさらに読むことができます。
失敗したコードサンプルの補足として。 g ++はこれについて次のように警告します
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)
UNICODE
定義Windows UNICODEビルドはTCHAR
などで定義され、wchar_t
などで定義されます。UNICODE
でビルドとして定義されていない場合、TCHAR
で定義されたchar
など。これらのUNICODE
および_UNICODE
定義は、すべての "T
"文字列タイプ ;に影響します。 LPTSTR
、LPCTSTR
およびそのヘラジカ。
UNICODE
が定義された1つのライブラリを構築し、UNICODE
が定義されていないプロジェクトでリンクしようとすると、TCHAR
の定義に不一致があるため、リンカーエラーが発生します。 char
vs. wchar_t
。
通常、エラーにはchar
またはwchar_t
派生型の値を持つ関数が含まれますが、これらにはstd::basic_string<>
なども含まれます。コード内の影響を受ける関数を参照すると、多くの場合、TCHAR
またはstd::basic_string<TCHAR>
などへの参照があります。これは、コードが元々UNICODEとMulti -バイト文字(または「狭い」)ビルド。
これを修正するには、UNICODE
(および_UNICODE
)の一貫した定義を使用して、必要なすべてのライブラリとプロジェクトをビルドします。
これは、次のいずれかで実行できます。
#define UNICODE
#define _UNICODE
またはプロジェクト設定で;
プロジェクトのプロパティ>一般>プロジェクトのデフォルト>文字セット
または、コマンドラインで。
/DUNICODE /D_UNICODE
UNICODEを使用するつもりがない場合は、代替も適用できます。定義が設定されていないこと、および/またはプロジェクトで複数文字設定が使用され、一貫して適用されていることを確認してください。
「リリース」ビルドと「デバッグ」ビルドの間でも一貫性を保つことを忘れないでください。
ヘッダファイルとそれに関連する共有ライブラリ(.libファイル)が同期しなくなると、リンカエラーが発生する可能性があります。説明させてください。
リンカーはどのように機能しますか?リンカは、シグネチャを比較することによって、(ヘッダーで宣言された)関数宣言と(共有ライブラリ内の)定義を照合します。リンカが完全に一致する関数定義を見つけられない場合は、リンカエラーが発生する可能性があります。
宣言と定義が一致しているように見えても、依然としてリンカーエラーが発生する可能性はありますか。はい!それらはソースコードで同じように見えるかもしれませんが、それは本当にコンパイラが見るものに依存します。基本的にあなたはこのような状況になる可能性があります。
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
両方の関数宣言がソースコード内で同じに見えても、コンパイラによって実際には異なっていることに注意してください。
あなたはそのような状況で人がどのように終わるのか尋ねるかもしれません? パスを含める もちろん!共有ライブラリをコンパイルするときにインクルードパスがheader1.h
になり、あなた自身のプログラムでheader2.h
を使用することになった場合、何が起こったのか疑問に思ってヘッダーをスクラッチしたままにしておくでしょう。
これが現実の世界でどのように発生する可能性があるかの例を以下に説明します。
graphics.lib
とmain.exe
の2つのプロジェクトがあります。どちらのプロジェクトもcommon_math.h
に依存しています。ライブラリが次の関数をエクスポートしたとします。
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
そして、あなたは先に進み、あなた自身のプロジェクトにライブラリを含めます。
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
ブーム!リンカエラーが発生し、それが失敗した理由がわかりません。その理由は、共通ライブラリが同じinclude common_math.h
の異なるバージョンを使用しているからです(ここでは例として別のパスを含めることで明確にしましたが、必ずしも明確であるとは限りません。 ).
この例では、リンカがdraw()
を見つけることができなかったと言っていることに注意してください。実際には、ライブラリによってエクスポートされていることが明らかです。何が悪かったのか疑問に思って頭をかいているのに何時間も費やすことができました。問題は、パラメータ型がわずかに異なるため、リンカは異なるシグネチャを認識することです。この例では、vec3
は、コンパイラに関する限り、両方のプロジェクトで異なる型です。これは2つのわずかに異なるインクルードファイルから来ているために起こる可能性があります(インクルードファイルは2つの異なるバージョンのライブラリから来ている可能性があります)。
Visual Studioを使用している場合、DUMPBINはあなたの友達です。私は他のコンパイラが他の同様のツールを持っていると確信しています。
プロセスは次のようになります。
[1]プロジェクトとは、リンクされてライブラリまたは実行可能ファイルを生成する一連のソースファイルのことです。
編集1:理解しやすいように最初のセクションを書き直しました。何か他のものを修正する必要があるかどうか私に知らせるために下記にコメントしてください。ありがとうございます。
ビルドを「クリーン」にすると、以前のビルド、失敗したビルド、不完全なビルド、およびその他のビルドシステム関連のビルドの問題から取り残されている可能性がある「枯れ木」を除去できます。
一般的にIDEやbuildには何らかの "clean"機能が含まれますが、これは正しく設定されていないか(手動のmakefileなどで)、失敗することがあります).
「クリーン」が完了したら、「クリーン」が成功し、生成されたすべての中間ファイル(自動メークファイルなど)が正常に削除されたことを確認します。
このプロセスは最終的な手段と見なすことができますが、多くの場合、最初の良いステップです;特に、エラーに関連するコードが最近(ローカルまたはソースリポジトリから)追加された場合。
const
変数の宣言/定義に "extern"がありません(C++のみ)Cから来た人々にとって、C++のグローバルなconst
variablesが内部的な(あるいは静的な)リンケージを持っているのは驚きかもしれません。 Cでは、すべてのグローバル変数が暗黙的にextern
であるため(つまり、static
キーワードがない場合)、これは当てはまりませんでした。
例:
// 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
}
正しい方法は、ヘッダーファイルを使用してそれをfile2.cpp および file1.cppに含めることです。
extern const int test;
extern int test2;
あるいは、明示的なconst
を使用してfile1.cppでextern
変数を宣言できます。
これは複数の回答が受け入れられているかなり古い質問ですが、obscure"undefined reference to"エラーの解決方法を共有したいと思います。
std::filesystem::path
を参照するエイリアスを使用していました:C++ 17以降、ファイルシステムは標準ライブラリにありますが、私のプログラムはC++ 14でコンパイルする必要があるため、変数エイリアスを使用することにしました:
#if (defined _GLIBCXX_EXPERIMENTAL_FILESYSTEM) //is the included filesystem library experimental? (C++14 and newer: <experimental/filesystem>)
using path_t = std::experimental::filesystem::path;
#Elif (defined _GLIBCXX_FILESYSTEM) //not experimental (C++17 and newer: <filesystem>)
using path_t = std::filesystem::path;
#endif
Main.cpp、file.h、file.cppの3つのファイルがあるとします。
Main.cppとfile.hで使用されるdifferent librariesに注意してください。 main.cppは<filesystem>の後に「file.h」を#includeしたため、そこで使用されたファイルシステムのバージョンはthe C + +17 one。次のコマンドを使用してプログラムをコンパイルしていました。
$ g++ -g -std=c++17 -c main.cpp
-> main.cppをmain.oにコンパイルします
$ g++ -g -std=c++17 -c file.cpp
-> file.cppとfile.hをfile.oにコンパイルします
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> main.oとfile.oをリンクします
このようにany functionはfile.oに含まれ、required path_t
がmain.oのために「未定義の参照」エラーを出したmain.oで使用されます。はstd::filesystem::path
を参照しますが、file.oはstd::experimental::filesystem::path
を参照します。
これを修正するには、file.hの<experimental :: filesystem>を<filesystem>に変更する必要がありました。
Gccのデフォルトの動作は、すべてのシンボルが見えることです。ただし、翻訳単位がオプション-fvisibility=hidden
で作成されている場合、結果として得られる共有オブジェクトでは、__attribute__ ((visibility ("default")))
でマークされた関数/シンボルのみが外部にあります。
あなたが探しているシンボルが外部のものであるかどうか確認することができます:
# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL
隠れた/ローカルなシンボルは小文字のシンボル型のnm
で示されます、例えばコードセクションのための `Tの代わりにt
:
nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL
(C++が使用されている場合は)nm
をオプション-C
と共に使用して名前をデマングルすることもできます。
Windows-dllと同様に、パブリック関数にdefineを付けることができます。例えば、DLL_PUBLIC
は次のように定義されます。
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
これは、Windows/MSVC-versionにほぼ対応します。
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
より多くの 可視性に関する情報 がgcc wikiにあります。
翻訳単位が-fvisibility=hidden
でコンパイルされるとき、結果のシンボルはまだ外部リンケージを持ち(nm
で大文字のシンボルタイプで示されます)、オブジェクトファイルが静的ライブラリの一部になった場合、問題なく外部リンケージに使用できます。リンクは、オブジェクトファイルが共有ライブラリにリンクされている場合にのみローカルになります。
オブジェクトファイル内のどのシンボルが隠されているかを調べるには、次のコマンドを実行します。
>>> objdump -t XXXX.o | grep hidden
0000000000000000 g F .text 000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g F .text 000000000000000b .hidden HIDDEN_SYMBOL2
さまざまなアーキテクチャ
次のようなメッセージが表示されるかもしれません。
library machine type 'x64' conflicts with target machine type 'X86'
その場合、それは利用可能なシンボルがあなたがコンパイルしているものとは異なるアーキテクチャ用であることを意味します。
Visual Studioでは、これは間違った「プラットフォーム」が原因であり、適切なものを選択するか、適切なバージョンのライブラリをインストールする必要があります。
Linuxでは、それは間違ったライブラリフォルダが原因であるかもしれません(例えばlib64
の代わりにlib
を使う)。
MacOSでは、両方のアーキテクチャを同じファイルで出荷するという選択肢があります。リンクは両方のバージョンがあることを期待しているかもしれませんが、1つだけです。ライブラリが拾われる間違ったlib
/lib64
フォルダの問題でもあるかもしれません。
エラーが発生するのは、リンカが宣言ファイル内の定義に対する定義、つまりヘッダファイルまたは宣言ファイルからの定義を見つけられなかった場合です。実装が見つからないときに発生します。
コンパイル後、リンカはライブラリから、または仮想関数用またはテンプレート定義用の実装を検索しようとします。見つからない場合は、これらのエラーはリンカのアーキテクチャに従って発生します。
UbuntuにはDebianパッチが適用されていますgcc-4.4/ g ++ - 4.4 :このコードにはダメです(他のns2コード/パッチも同様)。
Ubuntu:mpolsr_umolsr-v1_ns235.patch
→ https://drive.google.com/file/d/0B7S...ew?usp=sharing (mpolsrコードで2017年作成、変更なし)
tar xvf ns-allinone-2.35_gcc5.tar.gz
https://drive.google.com/file/d/0B7S...ew?usp=sharing
cd ns-allinone-2.35/
patch -p0 < mpolsr_umolsr-v1_ns235.patch // umolsr version v1.0 is used
./install
// Stops with MPOLSR.cc
cd ns-2.35/
// Edit the Makefile line 37 to CPP = g++34 , and run make
make
// When 'make' stops with an mdart/* error, change to CPP = g++-4.4
make
gcc34 Ubuntu https://drive.google.com/file/d/0B7S255p3kFXNRTkzQnRSNXZ6UVU/view?usp=sharing
g ++ 34 Ubuntu https://drive.google.com/file/d/0B7S255p3kFXNV3J3bnVoWGNWdG8/view?usp=sharing
inline
指定子を使ってソースファイルで定義されます。例:-
main.cpp
#include "gum.h"
#include "foo.h"
int main()
{
gum();
foo f;
f.bar();
return 0;
}
foo.h(1)
#pragma once
struct foo {
void bar() const;
};
gum.h(1)
#pragma once
extern void gum();
foo.cpp(1)
#include "foo.h"
#include <iostream>
inline /* <- wrong! */ void foo::bar() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
gum.cpp(1)
#include "gum.h"
#include <iostream>
inline /* <- wrong! */ void gum()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
gum
(同様に、foo::bar
)がその定義においてinline
であると指定した場合、コンパイラはgum
をインライン化します(選択した場合)。
gum
の固有の定義を出さないので、gum
の定義を参照できるようなシンボルを生成しません。代わりにgum
へのすべての呼び出しをgum
のコンパイル済み本体のインラインコピーに置き換えます。その結果、ソースファイルgum.cpp
にgum
をインラインで定義すると、gum
へのすべての呼び出しがインライン化され、シンボルが定義されていないオブジェクトファイルgum.o
にコンパイルされます。 gum
を参照できます。 gum.o
を他のオブジェクトファイルと一緒にプログラムにリンクするとき。外部シンボルgum
への参照を作成するmain.o
では、リンカはそれらの参照を解決できません。そのため、リンケージは失敗します。
コンパイル:
g++ -c main.cpp foo.cpp gum.cpp
リンク:
$ g++ -o prog main.o foo.o gum.o
main.o: In function `main':
main.cpp:(.text+0x18): undefined reference to `gum()'
main.cpp:(.text+0x24): undefined reference to `foo::bar() const'
collect2: error: ld returned 1 exit status
gum
を呼び出すことができるすべてのソースファイルでコンパイラがその定義を参照できる場合にのみ、inline
をgum
として定義できます。これは、そのインライン定義がheaderファイルに存在する必要があることを意味します。あなたがコンパイルするすべてのソースファイルには、include _ gum
を呼び出すことができます。 2つのうちの1つをしてください:
定義をインライン化しないでください
ソースファイル定義からinline
指定子を削除します。
foo.cpp(2)
#include "foo.h"
#include <iostream>
void foo::bar() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
gum.cpp(2)
#include "gum.h"
#include <iostream>
void gum()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
それを再構築します。
$ g++ -c main.cpp foo.cpp gum.cpp
imk@imk-Inspiron-7559:~/develop/so/scrap1$ g++ -o prog main.o foo.o gum.o
imk@imk-Inspiron-7559:~/develop/so/scrap1$ ./prog
void gum()
void foo::bar() const
成功。
または正しくインライン -
ヘッダーファイル内のインライン定義
foo.h(2)
#pragma once
#include <iostream>
struct foo {
void bar() const { // In-class definition is implicitly inline
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
// Alternatively...
#if 0
struct foo {
void bar() const;
};
inline void foo::bar() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
#endif
gum.h(2)
#pragma once
#include <iostream>
inline void gum() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
foo.cpp
やgum.cpp
は必要ありません。
$ g++ -c main.cpp
$ g++ -o prog main.o
$ ./prog
void gum()
void foo::bar() const