連結器工具錯誤 LNK2001

未解析的外部符號 「 symbol

編譯的程式碼會參考或呼叫 符號 。 符號未定義于連結器所搜尋的任何程式庫或物件檔案中。

此錯誤訊息後面接著嚴重錯誤 LNK1120 。 若要修正錯誤LNK1120,請先修正所有LNK2001和LNK2019錯誤。

有許多方式可以取得LNK2001錯誤。 它們全都牽涉 到連結器無法 解析 的函式或變數的參考 ,或尋找其定義。 編譯器可以在程式碼未 宣告 符號,但未 定義 符號時識別。 這是因為定義可能位於不同的原始程式檔或程式庫中。 如果您的程式碼參考符號,但從未定義過,連結器會產生錯誤。

什麼是未解析的外部符號?

符號 是函式或全域變數的內部名稱。 這是在編譯的物件檔或程式庫中使用或定義的名稱形式。 全域變數定義于為它配置儲存體的物件檔案中。 函式定義于物件檔中,其中會放置函式主體的編譯器代碼。 外部符號是一個物件檔中參考的符號 ,但定義在不同的程式庫或物件檔中。 匯出的符號是由定義它的物件檔案或程式庫公開提供的符號

若要建立應用程式或 DLL,使用的每個符號都必須有定義。 連結器必須 解析 或尋找每個物件檔所參考之每個外部符號的相符定義。 當連結器無法解析外部符號時,就會產生錯誤。 這表示連結器在任何連結的檔案中找不到相符的匯出符號定義。

此錯誤可能發生:

  • 當專案遺漏程式庫的參考時(。LIB) 或 物件 (.OBJ) 檔案。 若要修正此問題,請將必要的程式庫或物件檔參考新增至您的專案。 如需詳細資訊,請參閱 lib Files 作為連結器輸入

  • 當專案具有程式庫的參考時(。LIB) 或 物件 (.OBJ) 檔案,接著需要來自另一個程式庫的符號。 即使您未呼叫導致相依性的函式,也可能會發生此情況。 若要修正此問題,請將其他程式庫的參考新增至您的專案。 如需詳細資訊,請參閱 瞭解傳統模型以連結:一起 乘坐符號。

  • 如果您使用 /NODEFAULTLIB /Zl 選項。 當您指定這些選項時,除非已明確包含這些選項,否則包含必要程式碼的程式庫不會連結至專案。 若要修正此問題,請明確包含您在連結命令列上使用的所有程式庫。 如果您在使用這些選項時看到許多遺漏的 CRT 或標準程式庫函式名稱,請在連結中明確包含 CRT 和標準程式庫 DLL 或程式庫檔案。

  • 如果您使用 /clr 選項進行編譯 。 可能有 遺漏的 .cctor 參考。 如需如何修正此問題的詳細資訊,請參閱 混合元件的 初始化。

  • 如果您在建置應用程式的偵錯版本時連結到發行模式程式庫。 同樣地,如果您使用 /MTd 或 /MDd 選項 定義 _DEBUG ,然後連結至發行程式庫,您應該預期有許多可能未解決的外部,以及其他問題。 使用偵錯程式庫連結發行模式組建也會導致類似的問題。 若要修正此問題,請確定您在偵錯組建中使用偵錯程式庫,以及零售組建中的零售程式庫。

  • 如果您的程式碼參考某個程式庫版本的符號,但您會連結不同版本的程式庫。 一般而言,您無法混合針對不同編譯器版本所建置的物件檔案或程式庫。 隨附于一個版本的程式庫可能包含在其他版本所包含程式庫中找不到的符號。 若要修正此問題,請先使用相同版本的編譯器建置所有物件檔案和程式庫,再將它們連結在一起。 如需詳細資訊,請參閱 Visual Studio 版本 之間的 C++ 二進位相容性。

  • 如果程式庫路徑已過期。 [工具 > 選項 > 專案 > VC++ 目錄 ] 對話方塊,在 [ 程式庫檔案 ] 選取下 ,可讓您變更程式庫搜尋順序。 專案 [屬性頁] 對話方塊中的 [連結器] 資料夾也可能包含可能過期的路徑。

  • 安裝新的 Windows SDK 時(可能位於不同的位置)。 程式庫搜尋順序必須更新,才能指向新的位置。 一般而言,您應該將新 SDK include 和 lib 目錄的路徑放在預設 Visual C++ 位置前面。 此外,包含內嵌路徑的專案可能仍指向有效但過期的舊路徑。 更新安裝至不同位置之新版本所新增之新功能的路徑。

  • 如果您在命令列建置,並已建立自己的環境變數。 確認工具、程式庫和標頭檔的路徑會移至一致的版本。 如需詳細資訊,請參閱 從命令列 使用 MSVC 工具組。

編碼問題

此錯誤可能是由下列原因所造成:

  • 原始程式碼或模組定義 (.def) 檔案中的不相符大小寫。 例如,如果您在一個 C++ 原始程式檔中命名變數 var1 ,並嘗試以另一個來源檔案的形式 VAR1 存取變數,就會產生此錯誤。 若要修正此問題,請使用一致的拼字和大小寫名稱。

  • 使用 函數內 嵌的專案。 當您在原始程式檔中將函 inline 式定義為 ,而不是在標頭檔中時,就會發生此情況。 在定義內嵌函式的來源檔案之外看不到。 若要修正此問題,請在宣告它們的標頭中定義內嵌函式。

  • 從 C++ 程式呼叫 C 函式,而不使用 extern "C" C 函式的宣告。 編譯器會針對 C 和 C++ 程式碼使用不同的內部符號命名慣例。 內部符號名稱是連結器解析符號時所尋找的內容。 若要修正此問題,請使用 extern "C" C++ 程式碼中使用的 C 函式所有宣告包裝函式,這會導致編譯器使用這些符號的 C 內部命名慣例。 編譯器選項 /Tp /Tc 會讓編譯器分別將檔案編譯為 C++ 或 C,無論副檔名為何。 這些選項可能會導致內部函式名稱與您預期的名稱不同。

  • 嘗試參考沒有外部連結的函式或資料。 在 C++ 中,除非明確指定為 extern ,否則內嵌函式和資料 const 具有內部連結。 若要修正此問題,請在定義來源檔案外部參考的符號上使用明確 extern 宣告。

  • 遺漏的 函式主體或變數 定義。 當您宣告但未在程式碼中定義變數、函式或類別時,此錯誤很常見。 編譯器只需要函式原型或 extern 變數宣告來產生物件檔,而不會發生錯誤,但是連結器無法解析對函式的呼叫或變數的參考,因為沒有保留函式程式碼或變數空間。 若要修正此問題,請務必在您連結的來源檔案或程式庫中定義每個參考的函式和變數。

  • 函式呼叫,會使用傳回和參數類型,或呼叫不符合函式定義中的函式類型或呼叫慣例。 在 C++ 物件檔中, Name 裝飾 會編碼呼叫慣例、類別或命名空間範圍,並傳回函式的 和 參數類型。 編碼的字串會成為最終裝飾函式名稱的一部分。 連結器會使用此名稱,從其他物件檔解析或比對函式的呼叫。 若要修正此問題,請確定函式宣告、定義和呼叫全都使用相同的範圍、類型和呼叫慣例。

  • 您在類別定義中包含函式原型時所呼叫的 C++ 程式碼,但不包含 函式的實 作。 若要修正此問題,請務必為您呼叫的所有類別成員提供定義。

  • 嘗試從抽象基類呼叫純虛擬函式。 純虛擬函式沒有基類實作。 若要修正此問題,請確定已實作所有稱為虛擬函式。

  • 嘗試使用該函式範圍內宣告的變數( 區域變數 )。 若要修正此問題,請移除不在範圍內之變數的參考,或將變數移至較高的範圍。

  • 當您建置 ATL 專案的 Release 版本時,會產生 CRT 啟動程式碼為必要訊息。 若要修正此問題,請執行下列其中一項:

    • 從預處理器清單中移除 _ATL_MIN_CRT ,定義以允許包含 CRT 啟動程式碼。 如需詳細資訊,請參閱 一般屬性頁 (Project)

    • 可能的話,請移除需要 CRT 啟動程式碼的 CRT 函式呼叫。 請改用其 Win32 對等專案。 例如,使用 lstrcmp 而不是 strcmp。 需要 CRT 啟動程式碼的已知函式是一些字串和浮點函式。

一致性問題

編譯器廠商之間,或甚至相同編譯器的不同版本之間,目前沒有 C++ 名稱裝飾 的標準 。 使用不同編譯器編譯的物件檔案可能不會使用相同的命名配置。 連結它們可能會導致錯誤LNK2001。

在不同模組上混合內嵌和非內嵌編譯選項 可能會導致LNK2001。 如果已開啟函式內嵌來建立 C++ 程式庫( /Ob1 /Ob2 ),但描述函式的對應標頭檔已關閉內嵌功能(沒有 inline 關鍵字),就會發生此錯誤。 若要修正此問題,請在其他原始程式檔中包含的標頭檔中定義 inline 函式。

如果您使用 #pragma inline_depth 編譯器指示詞,請確定您已設定 2 或更新版本 的值,並確定您也使用 /Ob1 /Ob2 編譯器選項。

如果您在建立僅限資源 DLL 時省略 LINK 選項 /NOENTRY,就會發生此錯誤。 若要修正此問題,請將 /NOENTRY 選項新增至連結命令。

如果您在專案中使用不正確的 /SUBSYSTEM 或 /ENTRY 設定,就會發生此錯誤。 例如,如果您撰寫主控台應用程式並指定 /SUBSYSTEM:WINDOWS,就會針對 WinMain 產生無法解析的外部錯誤。 若要修正此問題,請務必將選項與專案類型相符。 如需這些選項和進入點的詳細資訊,請參閱 /SUBSYSTEM /ENTRY 連結器選項。

匯出的 .def 檔案符號問題

找不到 .def 檔案中所列的匯出時,就會發生此錯誤。 可能是因為匯出不存在、拼字不正確,或使用 C++ 裝飾名稱。 .def 檔案不會採用裝飾名稱。 若要修正此問題,請移除不需要的匯出,並使用 extern "C" 匯出符號的宣告。

使用裝飾名稱來尋找錯誤

C++ 編譯器和連結器會使用 名稱裝飾 ,也稱為 名稱管理 。 名稱裝飾會在其符號名稱中編碼變數類型的額外資訊。 函式的符號名稱會編碼其傳回型別、參數類型、範圍和呼叫慣例。 這個裝飾名稱是連結器搜尋以解析外部符號的符號名稱。

如果函式或變數的宣告與函式或變數的定義不 完全 相符,則連結錯誤可能會產生。 這是因為任何差異會變成符號名稱的一部分以符合。 即使呼叫端程式碼和定義程式碼都使用相同的標頭檔,也會發生此錯誤。 其中一種方式是,如果您使用不同的編譯器旗標來編譯來源檔案。 例如,如果您的程式碼編譯成使用 __vectorcall 呼叫慣例,但您會連結至預期用戶端使用預設或 __fastcall 呼叫慣例來呼叫它的連結 __cdecl 庫。 在此情況下,符號不相符,因為呼叫慣例不同。

為了協助您找出原因,錯誤訊息會顯示兩個版本的名稱。 它會顯示 「易記名稱」、原始程式碼中使用的名稱,以及裝飾名稱 (括弧中)。 您不需要知道如何解譯裝飾名稱。 您仍然可以搜尋並比較它與其他裝飾名稱。 命令列工具可協助尋找並比較預期的符號名稱和實際符號名稱:

  • DUMPBIN 命令列工具的 /EXPORTS /SYMBOLS 選項在這裡很有用。 它們可協助您探索 .dll 和物件或程式庫檔案中定義的符號。 您可以使用符號清單來確認匯出的裝飾名稱符合連結器搜尋的裝飾名稱。

  • 在某些情況下,連結器只能報告符號的裝飾名稱。 您可以使用 UNDNAME 命令列工具來取得裝飾名稱的未編碼形式。

其他資源

如需詳細資訊,請參閱 Stack Overflow 問題 「什麼是未定義的參考/未解析的外部符號錯誤,以及如何修正?」