共用方式為


連結器工具錯誤 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 問題 「什麼是未定義的參考/未解析的外部符號錯誤,以及如何修正?」