リンカ ツール エラー LNK2001

外部シンボル "symbol" は未解決です

コンパイルされたコードは、"シンボル" への参照または呼び出しを行います。 シンボルは、リンカーによって検索されるライブラリまたはオブジェクト ファイルで定義されていません。

このエラー メッセージの後に、致命的なエラー LNK1120 が発生します。 エラー LNK1120 を修正するには、最初に LNK2001 と LNK2019 のエラーをすべて修正する必要があります。

LNK2001 エラーを取得するには、さまざまな方法があります。 これらはすべて、リンカーによって "解決" されなかった、または定義が見つからなかった関数や変数への "参照 "を含んでいます。 コンパイラでは、コードでシンボルが "宣言" されていない場合は識別できますが、"定義" されていない場合は識別できません。 これは、定義が別のソース ファイルまたはライブラリにある可能性があるためです。 コードでシンボルが参照されている一方で定義されていない場合、リンカーではエラーが生成されます。

未解決の外部シンボルとは

"シンボル" は、関数またはグローバル変数の内部名です。 これは、コンパイルされたオブジェクト ファイルまたはライブラリで使用/定義されている名前の形式です。 グローバル変数は、ストレージが割り当てられているオブジェクト ファイルで定義されます。 関数は、関数本体のコンパイル済みコードが配置されるオブジェクト ファイル内で定義されます。 "外部シンボル" は 1 つのオブジェクト ファイルで参照されますが、別のライブラリまたはオブジェクト ファイルで定義されます。 "エクスポートされたシンボル" とは、それが定義されているオブジェクト ファイルまたはライブラリによって公開されるものです。

アプリケーションまたは DLL を作成するには、使用するすべてのシンボルが定義されている必要があります。 リンカーでは、各オブジェクト ファイルによって参照されるすべての外部シンボルを "解決"、つまり一致する定義を見つける必要があります。 外部シンボルを解決できない場合、リンカーではエラーが生成されます。 つまり、リンカーによって、リンクされたすべてのファイルで、一致するエクスポートされたシンボルの定義が検出されなかった場合です。

このエラーは、次の場合に発生することがあります。

  • プロジェクトに、ライブラリ (.LIB) またはオブジェクト (.OBJ) ファイルへの参照がない場合。 この問題を解決するには、目的のライブラリまたはオブジェクト ファイルへの参照をプロジェクトに追加します。 詳細については、リンカー入力としての LIB ファイルに関するページを参照してください。

  • プロジェクトにライブラリ (.LIB)、または別のライブラリのシンボルを必要とするオブジェクト (.OBJ) ファイルへの参照がある場合。 これは、依存関係を引き起こす関数を呼び出さない場合でも発生する可能性があります。 この問題を解決するには、他のライブラリへの参照をプロジェクトに追加します。 詳細については、「リンクのための古典的なモデルとは: シンボルを使用する」を参照してください。

  • /NODEFAULTLIB または /Zl のオプションを使用する場合。 これらのオプションを指定した場合、明示的に含めない限り、必須コードを含むライブラリはプロジェクトにリンクされません。 この問題を解決するには、リンク コマンド ラインで使用するすべてのライブラリを明示的に含めます。 これらのオプションを使用するときに、不足している CRT や標準ライブラリの関数名が多数表示される場合は、CRT および標準ライブラリの DLL またはライブラリ ファイルをリンクに明示的に含めます。

  • /clr オプションを使用してコンパイルする場合。 .cctor への参照がない可能性があります。 この問題を解決する方法の詳細については、「混在アセンブリの初期化」を参照してください。

  • アプリケーションのデバッグ バージョンをビルドする際に、リリース モード ライブラリにリンクする場合。 同様に、/MTd/MDd オプションを使用するか、または _DEBUG を定義してリリース ライブラリにリンクする場合は、他の問題として、潜在的に未解決の外部参照が多くなる可能性があります。 デバッグ ライブラリを使用してリリース モード ビルドをリンクした場合も、同様の問題が発生します。 この問題を解決するには、デバッグ ビルドでデバッグ ライブラリを使用し、製品ビルドで製品ライブラリを使用していることを確認します。

  • コードで 1 つのライブラリ バージョンのシンボルを参照している一方で、別のバージョンのライブラリにリンクしている場合。 一般に、異なるバージョンのコンパイラ用にビルドされたオブジェクト ファイルやライブラリを混在させることはできません。 1 つのバージョンで出荷されるライブラリには、他のバージョンに含まれているライブラリには見つからないシンボルが含まれている場合があります。 この問題を解決するには、同じバージョンのコンパイラを使用してすべてのオブジェクト ファイルとライブラリをビルドしてから、それらをリンクします。 詳細については、「Visual Studio のバージョン間の C++ バイナリ互換性」を参照してください。

  • ライブラリのパスが古くなっている場合。 [ツール] > [オプション] > [プロジェクト] > [VC++ ディレクトリ] ダイアログでは、[ライブラリ ファイル] でライブラリの検索順序を変更できます。 プロジェクトの [プロパティ ページ] ダイアログ ボックスの [リンカー] フォルダーには、古いパスが含まれている場合もあります。

  • 新しい Windows SDK が (場合によっては別の場所に) インストールされている場合。 ライブラリの検索順序は、新しい場所を指すように更新する必要があります。 通常は、新しい SDK のインクルードと lib のディレクトリへのパスを既定の Visual C++ の場所の前に配置する必要があります。 また、埋め込みパスを含むプロジェクトが、有効ではあるが以前に使用されていた古いパスを指している可能性もあります。 別の場所にインストールされている新しいバージョンによって追加された新しい機能のパスを更新します。

  • コマンド ラインでビルドし、独自の環境変数を作成した場合。 ツール、ライブラリ、ヘッダー ファイルへのパスが、一貫性のあるバージョンを指していることを確認します。 詳細については、「コマンド ラインから MSVC ツールセットを使用する」を参照してください。

コーディングに関する問題

このエラーは、次のことが原因で発生する場合があります。

  • ソース コードまたはモジュール定義 (.def) ファイルで大文字と小文字が一致していない。 たとえば、ある C++ ソース ファイルで変数に var1 という名前を付けて、別のファイルで VAR1 と指定してアクセスしようとすると、このエラーが生成されます。 この問題を解決するには、名前のスペルと大文字/小文字に一貫性を持たせるようにします。

  • プロジェクトで関数インライン展開を使用している。 これは、ヘッダー ファイルではなく、ソース ファイルで関数を inline として定義した場合に発生する可能性があります。 インライン関数は、それが定義されているソース ファイルの外部では表示できません。 この問題を解決するには、インライン関数を、それが宣言されているヘッダー内で定義します。

  • C 関数の extern "C" 宣言を使用せずに、C++ プログラムから C 関数を呼び出している。 コンパイラでは、C および C++ コードに対して異なる内部シンボル名前付け規則が使用されます。 内部シンボル名は、シンボルを解決するときにリンカーによって検索されるものです。 この問題を解決するには、C++ コードで使用される C 関数のすべての宣言に extern "C" ラッパーを使用します。こうすることで、コンパイラでは、これらのシンボルに対して C 内部の名前付け規則が使用されます。 コンパイラ オプション /Tp および /Tc を使用すると、コンパイラでは、ファイル名の拡張子に関係なく、それぞれ C++ または C としてファイルがコンパイルされます。 これらのオプションを使用した場合、内部関数名が予期したものと異なることがあります。

  • 外部リンケージが含まれていない関数またはデータを参照しようとした。 C++ では、明示的に extern として指定されている場合を除き、インライン関数と const データには内部リンケージが含まれます。 この問題を解決するには、定義元のソース ファイル外で参照されるシンボルに対して明示的な extern 宣言を使用します。

  • 関数本体または変数定義がない。 このエラーは、通常、コード内で変数、関数、またはクラスを宣言した一方で、それらを定義していない場合に発生します。 コンパイラでは、エラーなしでオブジェクト ファイルを生成するには関数プロトタイプまたは extern 変数宣言のみが必要ですが、リンカーでは、関数コードまたは変数空間が予約されていないため、関数の呼び出しまたは変数への参照が解決されません。 この問題を解決するには、リンクするソース ファイルまたはライブラリ内で、参照されているすべての関数と変数を定義してください。

  • 関数呼び出しで、関数の定義にあるものと一致しない戻り値とパラメーターの型、または呼び出し規約が使用されている。 C++ オブジェクト ファイルでは、名前の装飾によって、呼び出し規則、クラスまたは名前空間スコープ、関数の戻り値とパラメーター型がエンコードされます。 エンコードされた文字列は、装飾された最後の関数名の一部になります。 リンカーでは、この名前を使用して、他のオブジェクト ファイルから関数の呼び出しが解決 (照合) されます。 この問題を解決するには、関数の宣言、定義、呼び出しすべてにおいて、同じスコープ、型、呼び出し規約が使用されていることを確認します。

  • 呼び出した C++ コード (関数プロトタイプをクラス定義に含めた一方で、関数の実装を含めていない場合)。 この問題を解決するには、呼び出すすべてのクラス メンバーに定義を指定してください。

  • 抽象基底クラスから純粋仮想関数を呼び出そうとした。 純粋仮想関数には基底クラスの実装がありません。 この問題を解決するには、呼び出されたすべての仮想関数が実装されていることを確認します。

  • 関数内で宣言された変数 (ローカル変数) をその関数のスコープ外で使用しようとした。 この問題を解決するには、スコープ内にない変数への参照を削除するか、変数を上位のスコープに移動します。

  • ATL プロジェクトのリリース バージョンをビルドする際に、CRT スタートアップ コードが必要であることを示すメッセージが生成される。 この問題を解決するには、次のいずれかの操作を行います。

    • プリプロセッサ定義の一覧から _ATL_MIN_CRT を削除して、CRT スタートアップ コードを含めることができるようにします。 詳細については、「[全般] プロパティ ページ (プロジェクト)」を参照してください。

    • 可能であれば、CRT スタートアップ コードを必要とする CRT 関数の呼び出しを削除します。 代わりに、Win32 に相当するものを使用してください。 たとえば、strcmp の代わりに lstrcmp を使用します。 CRT スタートアップ コードを必要とする既知の関数は、文字列と浮動小数点関数の一部です。

一貫性に関する問題

現在、コンパイラ販売元間または同じコンパイラのバージョン間でさえも、C++ の名前の装飾に関する標準がありません。 異なるコンパイラでコンパイルされたオブジェクト ファイルでは、同じ名前付けスキームを使用できないことがあります。 これらをリンクすると、エラー LNK2001 が発生する可能性があります。

異なるモジュールでインラインと非インラインのコンパイル オプションを混在させると、LNK2001 が発生する可能性があります。 関数のインライン化をオン (/Ob1 または /Ob2) にして C++ ライブラリを作成しても、その関数を記述した対応するヘッダー ファイルのインライン化がオフ (inline キーワードなし) になっていると、このエラーが発生します。 この問題を解決するには、他のソース ファイルに含めるヘッダー ファイルに関数 inline を定義します。

#pragma inline_depth のコンパイラ ディレクティブを使用している場合は、2 以上の値を設定していることを確認します。また、コンパイラ オプション /Ob1 または /Ob2 を使用していることも確認します。

このエラーは、リソースのみの DLL を作成する際にリンク オプション /NOENTRY を省略した場合に発生する可能性があります。 この問題を解決するには、/NOENTRY オプションをリンク コマンドに追加します。

このエラーは、プロジェクトで不正な /SUBSYSTEM または /ENTRY 設定を使用した場合に発生する可能性があります。 たとえば、コンソール アプリケーションを作成して /SUBSYSTEM:WINDOWS を指定した場合、WinMain に対して未解決の外部エラーが生成されます。 この問題を解決するには、オプションがプロジェクト タイプと一致していることを確認してください。 これらのオプションとエントリ ポイントの詳細については、リンカー オプション /SUBSYSTEM/ENTRY に関するページを参照してください。

エクスポートされた .def ファイルのシンボルに関する問題

このエラーは、.def ファイルに一覧表示されているエクスポートが見つからない場合に発生します。 これは、エクスポートが存在しないか、スペルが間違っているか、C++ の装飾名が使用されていることによります。 .def ファイルでは装飾名を使用できません。 この問題を解決するには、不要なエクスポートを削除し、エクスポートされたシンボルに extern "C" 宣言を使用します。

装飾名を使用してエラーを見つける

C++ コンパイラとリンカーでは、名前の装飾が使用されます。これは、"名前装飾" とも呼ばれます。 名前の装飾では、シンボル名に含まれる変数の型についての追加情報がエンコードされます。 関数のシンボル名では、その戻り値の型、パラメーターの型、スコープ、呼び出し規則などがエンコードされます。 この装飾名は、外部シンボルを解決する際にリンカーが検索するシンボル名です。

関数または変数の宣言が関数または変数の定義と "完全" に一致しない場合、リンク エラーが発生する可能性があります。 これは、一致させるシンボル名の一部に違いがあるためです。 このエラーは、呼び出しコードと定義するコードの両方で同じヘッダー ファイルが使用されている場合でも発生する可能性があります。 これが発生するケースの 1 つに、異なるコンパイラ フラグを使用してソース ファイルをコンパイルした場合があります。 たとえば、__vectorcall の呼び出し規則を使用するようにコードをコンパイルした一方で、クライアントが既定の __cdecl または __fastcall の呼び出し規則を使用して呼び出しを行うことを想定しているライブラリにリンクする場合などです。 この場合、呼び出し規則が異なるため、シンボルは一致しません。

原因を見つけられるように、エラー メッセージには 2 つのバージョンの名前が表示されます。 "フレンドリ名" (ソース コードで使用される名前) と装飾名 (かっこ内) の両方が表示されます。 装飾名の解釈方法を理解している必要はありません。 検索して、他の装飾名と比較することができます。 コマンド ライン ツールを使用すると、期待されるシンボル名と実際のシンボル名を見つけて、比較することができます。

  • ここでは、DUMPBIN コマンド ライン ツールの /EXPORTS/SYMBOLS のオプションが便利です。 これらを使用すると、.dll とオブジェクトまたはライブラリ ファイルで定義されているシンボルを確認できます。 シンボル リストを使用して、エクスポートされた装飾名が、リンカーで検索される装飾名と一致することを確認できます。

  • 場合によっては、リンカーでは、シンボルの装飾名のみを報告できます。 UNDNAME コマンド ライン ツールを使用すると、装飾名の装飾されていない形式を取得できます。

その他のリソース

詳細については、「スタック オーバーフロー」の質問「未定義の参照/未解決の外部シンボルというエラーの意味と解決方法を教えてください」を参照してください。