c++ - यवस - एक अनिर्धारित संदर्भ/अनसुलझा बाहरी प्रतीक त्रुटि क्या है और मैं इसे कैसे ठीक करूं?




व्यवस्थित त्रुटियों (18)

When your include paths are different

Linker errors can happen when a header file and its associated shared library (.lib file) go out of sync. Let me explain.

How do linkers work? The linker matches a function declaration (declared in the header) with its definition (in the shared library) by comparing their signatures. You can get a linker error if the linker doesn't find a function definition that matches perfectly.

Is it possible to still get a linker error even though the declaration and the definition seem to match? हाँ! They might look the same in source code, but it really depends on what the compiler sees. Essentially you could end up with a situation like this:

// header1.h
typedef int Number;
void foo(Number);

// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically

Note how even though both the function declarations look identical in source code, but they are really different according to the compiler.

You might ask how one ends up in a situation like that? Include paths of course! If when compiling the shared library, the include path leads to header1.h and you end up using header2.h in your own program, you'll be left scratching your header wondering what happened (pun intended).

An example of how this can happen in the real world is explained below.

Further elaboration with an example

I have two projects: graphics.lib and main.exe . Both projects depend on common_math.h . Suppose the library exports the following function:

// graphics.lib    
#include "common_math.h" 

void draw(vec3 p) { ... } // vec3 comes from common_math.h

And then you go ahead and include the library in your own project.

// main.exe
#include "other/common_math.h"
#include "graphics.h"

int main() {
    draw(...);
}

Boom! You get a linker error and you have no idea why it's failing. The reason is that the common library uses different versions of the same include common_math.h (I have made it obvious here in the example by including a different path, but it might not always be so obvious. Maybe the include path is different in the compiler settings).

Note in this example, the linker would tell you it couldn't find draw() , when in reality you know it obviously is being exported by the library. You could spend hours scratching your head wondering what went wrong. The thing is, the linker sees a different signature because the parameter types are slightly different. In the example, vec3 is a different type in both projects as far as the compiler is concerned. This could happen because they come from two slightly different include files (maybe the include files come from two different versions of the library).

Debugging the linker

DUMPBIN is your friend, if you are using Visual Studio. I'm sure other compilers have other similar tools.

The process goes like this:

  1. Note the weird mangled name given in the linker error. (eg. [email protected]@XYZ).
  2. Dump the exported symbols from the library into a text file.
  3. Search for the exported symbol of interest, and notice that the mangled name is different.
  4. Pay attention to why the mangled names ended up different. You would be able to see that the parameter types are different, even though they look the same in the source code.
  5. Reason why they are different. In the example given above, they are different because of different include files.

[1] By project I mean a set of source files that are linked together to produce either a library or an executable.

EDIT 1: Rewrote first section to be easier to understand. Please comment below to let me know if something else needs to be fixed. धन्यवाद!

अपरिभाषित संदर्भ / अनसुलझा बाहरी प्रतीक त्रुटियां क्या हैं? आम कारण क्या हैं और उन्हें कैसे ठीक / रोकें?

अपने स्वयं के संपादन / जोड़ने के लिए स्वतंत्र महसूस करें।


कक्षा के सदस्य:

एक शुद्ध virtual विनाशक को एक कार्यान्वयन की आवश्यकता है।

एक विनाशक शुद्ध घोषित करने के लिए अभी भी आपको इसे परिभाषित करने की आवश्यकता है (नियमित कार्य के विपरीत):

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

ऐसा इसलिए होता है क्योंकि ऑब्जेक्ट को पूरी तरह नष्ट कर दिया जाता है, इसलिए बेस क्लास विनाशकों को बुलाया जाता है, इसलिए एक परिभाषा की आवश्यकता होती है।

virtual विधियों को या तो शुद्ध या परिभाषित किया जाना चाहिए।

यह गैर- virtual विधियों के समान है, जिसमें कोई परिभाषा नहीं है, शुद्ध तर्क यह है कि शुद्ध घोषणा एक डमी vtable उत्पन्न करती है और आपको फ़ंक्शन का उपयोग किये बिना लिंकर त्रुटि मिल सकती है:

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() को शुद्ध के रूप में घोषित करें:

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 डेटा सदस्य के लिए एक प्रारंभकर्ता प्रदान किया जा सकता है; हालांकि, इस सदस्य के ओडीआर-उपयोग को अभी भी उपरोक्त वर्णित नामस्थान स्कोप परिभाषा की आवश्यकता होगी। सी ++ 11 सभी static const डेटा सदस्यों के लिए कक्षा के अंदर प्रारंभिकरण की अनुमति देता है।


Befriending templates...

Given the code snippet of a template type with a friend operator (or function);

template <typename T>
class Foo {
    friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};

The operator<< is being declared as a non-template function. For every type T used with Foo , there needs to be a non-templated operator<< . For example, if there is a type Foo<int> declared, then there must be an operator implementation as follows;

std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}

Since it is not implemented, the linker fails to find it and results in the error.

To correct this, you can declare a template operator before the Foo type and then declare as a friend, the appropriate instantiation. The syntax is a little awkward, but is looks as follows;

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

The above code limits the friendship of the operator to the corresponding instantiation of Foo , ie the operator<< <int> instantiation is limited to access the private members of the instantiation of Foo<int> .

Alternatives include;

  • Allowing the friendship to extend to all instantiations of the templates, as follows;

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    
  • Or, the implementation for the operator<< can be done inline inside the class definition;

    template <typename T>
    class Foo {
        friend std::ostream& operator<<(std::ostream& os, const Foo& a)
        { /*...*/ }
        // ...
    };
    

Note , when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference ;

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not accessible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided...

There is further reading on template friends at cppreference and the C++ FAQ .

Code listing showing the techniques above .

As a side note to the failing code sample; g++ warns about this as follows

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)


Clean and rebuild

A "clean" of the build can remove the "dead wood" that may be left lying around from previous builds, failed builds, incomplete builds and other build system related build issues.

In general the IDE or build will include some form of "clean" function, but this may not be correctly configured (eg in a manual makefile) or may fail (eg the intermediate or resultant binaries are read-only).

Once the "clean" has completed, verify that the "clean" has succeeded and all the generated intermediate file (eg an automated makefile) have been successfully removed.

This process can be seen as a final resort, but is often a good first step ; especially if the code related to the error has recently been added (either locally or from the source repository).


Missing "extern" in const variable declarations/definitions (C++ only)

For people coming from C it might be a surprise that in C++ global const variables have internal (or static) linkage. In C this was not the case, as all global variables are implicitly extern (ie when the static keyword is missing).

उदाहरण:

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

correct would be to use a header file and include it in file2.cpp and file1.cpp

extern const int test;
extern int test2;

Alternatively one could declare the const variable in file1.cpp with explicit extern


Use the linker to help diagnose the error

Most modern linkers include a verbose option that prints out to varying degrees;

  • Link invocation (command line),
  • Data on what libraries are included in the link stage,
  • The location of the libraries,
  • Search paths used.

For gcc and clang; you would typically add -v -Wl,--verbose or -v -Wl,-v to the command line. More details can be found here;

For MSVC, /VERBOSE (in particular /VERBOSE:LIB ) is added to the link command line.


उचित पुस्तकालयों / ऑब्जेक्ट फ़ाइलों के खिलाफ लिंक करने या कार्यान्वयन फ़ाइलों को संकलित करने में विफलता

आम तौर पर, प्रत्येक अनुवाद इकाई एक ऑब्जेक्ट फ़ाइल उत्पन्न करेगी जिसमें उस अनुवाद इकाई में परिभाषित प्रतीकों की परिभाषाएं होंगी। उन प्रतीकों का उपयोग करने के लिए, आपको उन ऑब्जेक्ट फ़ाइलों के विरुद्ध लिंक करना होगा।

जीसीसी के तहत आप सभी ऑब्जेक्ट फ़ाइलों को निर्दिष्ट करेंगे जिन्हें कमांड लाइन में एक साथ जोड़ा जाना है, या कार्यान्वयन फ़ाइलों को एकसाथ संकलित करना है।

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

लाइब्रेरी नाम यहां लाइब्रेरी का विशिष्ट नाम है, बिना मंच-विशिष्ट परिवर्धन के। तो उदाहरण के लिए लिनक्स लाइब्रेरी फ़ाइलों पर आमतौर पर libfoo.so कहा जाता है लेकिन आप केवल -lfoo । विंडोज़ पर उसी फ़ाइल को foo.lib कहा जा सकता है, लेकिन आप एक ही तर्क का उपयोग करेंगे। आपको निर्देशिका को जोड़ना पड़ सकता है जहां उन फ़ाइलों को -L‹directory› का उपयोग करके पाया जा सकता है। -L या -L बाद एक जगह नहीं लिखना सुनिश्चित करें।

एक्सकोड के लिए : उपयोगकर्ता शीर्षलेख खोज पथ जोड़ें -> लाइब्रेरी खोज पथ जोड़ें -> प्रोजेक्ट फ़ोल्डर में वास्तविक लाइब्रेरी संदर्भ खींचें और छोड़ें।

एमएसवीएस के तहत, किसी प्रोजेक्ट में जोड़े गए फाइलों में स्वचालित रूप से उनकी ऑब्जेक्ट फ़ाइलें एक साथ जुड़ी होती हैं और एक lib फ़ाइल जेनरेट की जाएगी (सामान्य उपयोग में)। एक अलग परियोजना में प्रतीकों का उपयोग करने के लिए, आपको परियोजना सेटिंग्स में lib फ़ाइलों को शामिल करने की आवश्यकता होगी। यह प्रोजेक्ट गुणों के लिंकर सेक्शन में, Input -> Additional Dependencies । ( lib फ़ाइल में पथ को Linker -> General -> Additional Library Directories में जोड़ा जाना चाहिए) एक lib फ़ाइल के साथ प्रदान की गई किसी तृतीय-पक्ष लाइब्रेरी का उपयोग करते समय, ऐसा करने में विफलता आमतौर पर त्रुटि में परिणाम देती है।

यह भी हो सकता है कि आप फ़ाइल को संकलन में जोड़ना भूल जाते हैं, जिस स्थिति में ऑब्जेक्ट फ़ाइल उत्पन्न नहीं की जाएगी। जीसीसी में आप कमांड लाइन में फाइलें जोड़ देंगे। एमएसवीएस में प्रोजेक्ट में फ़ाइल जोड़ने से यह स्वचालित रूप से संकलित हो जाएगा (यद्यपि फाइलें मैन्युअल रूप से, निर्माण से व्यक्तिगत रूप से बाहर की जा सकती हैं)।

विंडोज प्रोग्रामिंग में, बताए गए संकेत चिह्न कि आपने एक आवश्यक लाइब्रेरी को लिंक नहीं किया है, यह है कि अनसुलझे प्रतीक का नाम __imp_ शुरू होता है। प्रलेखन में फ़ंक्शन का नाम देखें, और यह कहना चाहिए कि आपको कौन सी लाइब्रेरी का उपयोग करने की आवश्यकता है। उदाहरण के लिए, एमएसडीएन सूचना को प्रत्येक लाइब्रेरी के नीचे एक बॉक्स में "लाइब्रेरी" नामक अनुभाग में रखता है।


घोषित किया लेकिन एक चर या समारोह को परिभाषित नहीं किया।

एक सामान्य परिवर्तनीय घोषणा है

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

सावधान रहें कि आपके द्वारा कार्यान्वित किए गए फ़ंक्शन को आपके द्वारा घोषित किए गए एक से मेल खाता है। उदाहरण के लिए, आपके पास मेल नहीं खाए गए सीवी-क्वालीफायर हो सकते हैं:

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)

विसंगतियों के अन्य उदाहरणों में शामिल हैं

  • फ़ंक्शन / वेरिएबल एक नामस्थान में घोषित किया गया है, जिसे दूसरे में परिभाषित किया गया है।
  • कार्य / परिवर्तक वर्ग सदस्य के रूप में घोषित किया गया है, जिसे वैश्विक (या इसके विपरीत) के रूप में परिभाषित किया गया है।
  • फंक्शन रिटर्न टाइप, पैरामीटर नंबर और प्रकार, और कॉलिंग कन्वेंशन बिल्कुल बिल्कुल सहमत नहीं है।

कंपाइलर से त्रुटि संदेश आपको अक्सर वेरिएबल या फ़ंक्शन की पूरी घोषणा देता है जिसे घोषित किया गया था लेकिन कभी परिभाषित नहीं किया गया था। आपके द्वारा प्रदान की गई परिभाषा से इसकी तुलना करें। सुनिश्चित करें कि हर विवरण मेल खाता है।


टेम्पलेट कार्यान्वयन दिखाई नहीं दे रहा है।

अनन्यीकृत टेम्पलेट्स में उनकी परिभाषाएं उन सभी अनुवाद इकाइयों को दिखाई देनी चाहिए जो उनका उपयोग करती हैं। इसका मतलब है कि आप किसी टेम्पलेट की परिभाषा को कार्यान्वयन फ़ाइल में अलग नहीं कर सकते हैं। यदि आपको कार्यान्वयन को अलग करना होगा, तो सामान्य वर्कअराउंड में एक 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 की परिभाषा को हेडर फ़ाइल या किसी भी स्थान पर अनुवाद इकाई को दिखाई देनी चाहिए जो इसका उपयोग करती है।

विशिष्ट टेम्पलेट्स को कार्यान्वयन फ़ाइल में कार्यान्वित किया जा सकता है और कार्यान्वयन को दृश्यमान नहीं होना चाहिए, लेकिन विशेषज्ञता पहले घोषित की जानी चाहिए।

आगे स्पष्टीकरण और एक और संभावित समाधान (स्पष्ट तत्काल) के लिए इस प्रश्न और उत्तर को देखें


प्रतीक एक सी प्रोग्राम में परिभाषित किए गए थे और सी ++ कोड में उपयोग किए गए थे।

फ़ंक्शन (या वेरिएबल) void foo() को सी प्रोग्राम में परिभाषित किया गया था और आप इसे C ++ प्रोग्राम में उपयोग करने का प्रयास करते हैं:

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

सी ++ लिंकर को नामों को उलझाने की उम्मीद है, इसलिए आपको इस समारोह को घोषित करना होगा:

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

समान रूप से, एक सी प्रोग्राम में परिभाषित होने की बजाय, फ़ंक्शन (या चर) void foo() को C ++ में परिभाषित किया गया था लेकिन सी लिंकेज के साथ:

extern "C" void foo();

और आप सी ++ लिंकेज के साथ सी ++ प्रोग्राम में इसका उपयोग करने का प्रयास करते हैं।

यदि एक संपूर्ण लाइब्रेरी हेडर फ़ाइल में शामिल है (और सी कोड के रूप में संकलित किया गया था); इसमें शामिल होने की आवश्यकता होगी;

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

इसके अलावा यदि आप तृतीय पक्ष पुस्तकालयों का उपयोग कर रहे हैं तो सुनिश्चित करें कि आपके पास सही 32/64 बिट बाइनरी हैं


एक सी ++ प्रोग्राम संकलित करना कई चरणों में होता है, जैसा कि 2.2 द्वारा निर्दिष्ट किया गया है (संदर्भ के लिए किथ थॉम्पसन को क्रेडिट) :

अनुवाद के वाक्यविन्यास नियमों के बीच प्राथमिकता निम्नलिखित चरणों द्वारा निर्दिष्ट है [फुटनोट देखें]

  1. भौतिक स्रोत फ़ाइल वर्णों को कार्यान्वित-परिभाषित तरीके से मैप किया जाता है, मूल स्रोत वर्ण सेट (यदि आवश्यक हो तो अंतराल संकेतकों के लिए नए-पंक्ति वर्ण प्रस्तुत करना)। [कटाव]
  2. बैकस्लैश वर्ण (\) के प्रत्येक उदाहरण को तुरंत एक नए-पंक्ति वर्ण के बाद हटा दिया जाता है, भौतिक स्रोत लाइनों को लॉजिकल स्रोत लाइन बनाने के लिए विभाजित किया जाता है। [कटाव]
  3. स्रोत फ़ाइल preprocessing टोकन (2.5) और सफेद अंतरिक्ष वर्णों (टिप्पणियों सहित) के अनुक्रमों में विघटित है। [कटाव]
  4. प्रीप्रोकैसिंग निर्देश निष्पादित किए जाते हैं, मैक्रो इनवॉक्शंस का विस्तार किया जाता है, और _Pragma unary ऑपरेटर अभिव्यक्ति निष्पादित की जाती है। [कटाव]
  5. प्रत्येक स्रोत चरित्र एक वर्ण शाब्दिक या एक स्ट्रिंग शाब्दिक में सदस्य सेट करता है, साथ ही साथ प्रत्येक अक्षर अनुक्रम और वर्णक अक्षर में सार्वभौमिक-वर्ण-नाम या गैर-कच्चे स्ट्रिंग अक्षर का नाम निष्पादन चरित्र सेट के संबंधित सदस्य में परिवर्तित हो जाता है; [कटाव]
  6. निकट स्ट्रिंग शाब्दिक टोकन concatenated हैं।
  7. टोकन को अलग करने वाले व्हाइट-स्पेस वर्ण अब महत्वपूर्ण नहीं हैं। प्रत्येक प्रीप्रोकैसिंग टोकन को टोकन में परिवर्तित किया जाता है। (2.7)। परिणामी टोकन वाक्य रचनात्मक रूप से और अर्थात् विश्लेषण और अनुवाद इकाई के रूप में अनुवादित हैं। [कटाव]
  8. अनुवादित अनुवाद इकाइयों और तत्काल इकाइयों को निम्नानुसार जोड़ा जाता है: [एसएनआईपी]
  9. सभी बाहरी इकाई संदर्भ हल हो गए हैं। लाइब्रेरी घटकों को मौजूदा अनुवाद में परिभाषित इकाइयों के बाहरी संदर्भों को पूरा करने के लिए जोड़ा जाता है। ऐसे सभी अनुवादक आउटपुट को एक प्रोग्राम छवि में एकत्र किया जाता है जिसमें निष्पादन वातावरण में निष्पादन के लिए आवश्यक जानकारी होती है। (जोर मेरा)

[फुटनोट] कार्यान्वयन का व्यवहार करना चाहिए जैसे कि ये अलग-अलग चरण होते हैं, हालांकि व्यवहार में विभिन्न चरणों को एक साथ जोड़ा जा सकता है।

निर्दिष्ट त्रुटियां संकलन के इस अंतिम चरण के दौरान होती हैं, जिन्हें आमतौर पर लिंकिंग के रूप में जाना जाता है। इसका मूल रूप से मतलब है कि आपने ऑब्जेक्ट फाइलों या पुस्तकालयों में कार्यान्वयन फ़ाइलों का एक समूह संकलित किया है और अब आप उन्हें एक साथ काम करने के लिए प्राप्त करना चाहते हैं।

कहें कि आपने a प्रतीक को a a.cpp में परिभाषित किया है। अब, b.cpp ने उस प्रतीक को घोषित किया और इसका इस्तेमाल किया। लिंक करने से पहले, यह मानता है कि उस प्रतीक को कहीं परिभाषित किया गया था, लेकिन यह अभी तक परवाह नहीं करता है। लिंकिंग चरण प्रतीक खोजने और इसे सही ढंग से b.cpp से जोड़ने के लिए b.cpp (ठीक है, वास्तव में उस ऑब्जेक्ट या लाइब्रेरी के लिए जो इसका उपयोग करता है)।

यदि आप माइक्रोसॉफ्ट विजुअल स्टूडियो का उपयोग कर रहे हैं, तो आप देखेंगे कि प्रोजेक्ट .lib फाइलें उत्पन्न करते हैं। इनमें निर्यात किए गए प्रतीकों की एक तालिका, और आयातित प्रतीकों की एक तालिका होती है। आयातित प्रतीकों को आपके द्वारा लिंक किए गए पुस्तकालयों के खिलाफ हल किया जाता है, और निर्यात किए गए प्रतीकों को पुस्तकालयों के लिए प्रदान किया जाता है जो कि .lib (यदि कोई हो) का उपयोग करते हैं।

अन्य कंपाइलर्स / प्लेटफॉर्म के लिए समान तंत्र मौजूद हैं।

सामान्य त्रुटि संदेश error LNK2001 , error LNK1120 , Microsoft Visual Studio के लिए error LNK2019 और जीसीसी के undefined reference to प्रतीक नाम के undefined reference to

कोड:

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

जीसीसी के साथ निम्नलिखित त्रुटियां उत्पन्न करेगा:

/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

और माइक्रोसॉफ्ट विजुअल स्टूडियो के साथ समान त्रुटियां:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" ([email protected]@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" ([email protected]@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" ([email protected]@[email protected])
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" ([email protected]@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

आम कारणों में शामिल हैं:

  • उचित पुस्तकालयों / ऑब्जेक्ट फ़ाइलों के खिलाफ लिंक करने या कार्यान्वयन फ़ाइलों को संकलित करने में विफलता
  • घोषित और अपरिभाषित चर या समारोह।
  • कक्षा के प्रकार के सदस्यों के साथ आम मुद्दे
  • टेम्पलेट कार्यान्वयन दिखाई नहीं दे रहा है।
  • प्रतीक एक सी प्रोग्राम में परिभाषित किए गए थे और सी ++ कोड में उपयोग किए गए थे।
  • मॉड्यूल / डीएलएल में सही ढंग से आयात / निर्यात विधियों / वर्गों। (एमएसवीएस विशिष्ट)
  • परिपत्र पुस्तकालय निर्भरता
  • 'WinMain @ 16' के अपरिभाषित संदर्भ
  • परस्पर निर्भर पुस्तकालय आदेश
  • एक ही नाम की एकाधिक स्रोत फाइलें
  • #pragma (माइक्रोसॉफ्ट विजुअल स्टूडियो) का उपयोग करते समय mlyping या .lib एक्सटेंशन सहित नहीं
  • टेम्पलेट दोस्तों के साथ समस्याएं
  • असंगत UNICODE परिभाषाएं

मान लीजिए कि आपके पास सी ++ में लिखी गई एक बड़ी परियोजना है जिसमें हजारों .cpp फ़ाइलें हैं और एक हजार .h फाइलें हैं। और मान लें कि यह परियोजना दस स्थिर पुस्तकालयों पर भी निर्भर करती है। मान लें कि हम विंडोज पर हैं और हम विजुअल स्टूडियो 20xx में हमारी परियोजना का निर्माण करते हैं। When you press Ctrl + F7 Visual Studio to start compiling the whole solution ( suppose we have just one project in the solution )

What's the meaning of compilation ?

  • Visual Studio search into file .vcxproj and start compiling each file which has the extension .cpp. Order of compilation is undefined.So you must not assume that the file main.cpp is compiled first
  • If .cpp files depends on additional .h files in order to find symbols that may or may not be defined in the file .cpp
  • If exists one .cpp file in which the compiler could not find one symbol, a compiler time error raises the message Symbol x could not be found
  • For each file with extension .cpp is generated an object file .o and also Visual Studio writes the output in a file named ProjectName.Cpp.Clean.txt which contains all object files that must be processed by the linker.

The Second step of compilation is done by Linker.Linker should merge all the object file and build finally the output ( which may be an executable or a library)

Steps In Linking a project

  • Parse all the object files and find the definition which was only declared in headers ( eg: The code of one method of a class as is mentioned in previous answers, or event the initialization of a static variable which is member inside a class)
  • If one symbol could not be found in object files he also is searched in Additional Libraries.For adding a new library to a project Configuration properties -> VC++ Directories -> Library Directories and here you specified additional folder for searching libraries and Configuration properties -> Linker -> Input for specifying the name of the library. -If the Linker could not find the symbol which you write in one .cpp he raises a linker time error which may sound like error LNK2001: unresolved external symbol "void __cdecl foo(void)" ([email protected]@YAXXZ)

Observation

  1. Once the Linker find one symbol he doesn't search in other libraries for it
  2. The order of linking libraries does matter .
  3. If Linker finds an external symbol in one static library he includes the symbol in the output of the project.However, if the library is shared( dynamic ) he doesn't include the code ( symbols ) in output, but Run-Time crashes may occur

How To Solve this kind of error

Compiler Time Error :

  • Make sure you write your c++ project syntactical correct.

Linker Time Error

  • Define all your symbol which you declare in your header files
  • Use #pragma once for allowing compiler not to include one header if it was already included in the current .cpp which are compiled
  • Make sure that your external library doesn't contain symbols that may enter into conflict with other symbols you defined in your header files
  • When you use the template to make sure you include the definition of each template function in the header file for allowing the compiler to generate appropriate code for any instantiations.

यह सबसे भ्रमित त्रुटि संदेशों में से एक है कि प्रत्येक वीसी ++ प्रोग्रामर ने समय और समय फिर से देखा है। चलिए पहले चीजों को स्पष्ट करते हैं।

ए प्रतीक क्या है? संक्षेप में, एक प्रतीक एक नाम है। यह एक चर नाम, एक समारोह का नाम, एक वर्ग का नाम, एक टाइपिफ़ नाम, या सी ++ भाषा से संबंधित उन नामों और संकेतों को छोड़कर कुछ भी हो सकता है। यह उपयोगकर्ता निर्भरता पुस्तकालय (अन्य उपयोगकर्ता परिभाषित) द्वारा परिभाषित या पेश किया गया है।

बी बाहरी क्या है? वीसी ++ में, प्रत्येक स्रोत फ़ाइल (.cpp, .c, आदि) को अनुवाद इकाई के रूप में माना जाता है, संकलक एक समय में एक इकाई को संकलित करता है, और वर्तमान अनुवाद इकाई के लिए एक ऑब्जेक्ट फ़ाइल (.obj) उत्पन्न करता है। (ध्यान दें कि इस स्रोत फ़ाइल में शामिल प्रत्येक हेडर फ़ाइल को प्रीप्रोसेस्ड किया जाएगा और इसे इस अनुवाद इकाई के हिस्से के रूप में माना जाएगा) एक अनुवाद इकाई के भीतर सबकुछ आंतरिक माना जाता है, बाकी सब कुछ बाहरी के रूप में माना जाता है। सी ++ में, आप बाह्य, __declspec (dllimport) जैसे कीवर्ड का उपयोग कर बाहरी प्रतीक का संदर्भ दे सकते हैं।

सी "संकल्प" क्या है? हल करना एक लिंकिंग-टाइम शब्द है। लिंकिंग-टाइम में, लिंकर ऑब्जेक्ट फ़ाइलों में प्रत्येक प्रतीक के लिए बाहरी परिभाषा को खोजने का प्रयास करता है जो आंतरिक रूप से इसकी परिभाषा नहीं पाता है। इस खोज प्रक्रिया के दायरे में शामिल हैं:

  • संकलन समय में उत्पन्न सभी ऑब्जेक्ट फ़ाइलें
  • सभी पुस्तकालयों (.lib) जो या तो स्पष्ट रूप से या स्पष्ट रूप से इस भवन अनुप्रयोग की अतिरिक्त निर्भरताओं के रूप में निर्दिष्ट हैं।

इस खोज प्रक्रिया को संकल्प कहा जाता है।

डी। अंत में, अनजान बाहरी प्रतीक क्यों? यदि लिंकर को उस प्रतीक के लिए बाहरी परिभाषा नहीं मिलती है जिसमें आंतरिक रूप से कोई परिभाषा नहीं है, तो यह एक अनसुलझा बाहरी प्रतीक त्रुटि की रिपोर्ट करता है।

ई। एलएनके201 9 के संभावित कारण : अनसुलझा बाहरी प्रतीक त्रुटि। हम पहले से ही जानते हैं कि यह त्रुटि लिंकर के कारण बाहरी प्रतीकों की परिभाषा को खोजने में विफल रही है, संभावित कारणों को इस तरह हल किया जा सकता है:

  1. परिभाषा मौजूद है

उदाहरण के लिए, यदि हमारे पास a.cpp में परिभाषित foo नामक एक फ़ंक्शन है:

int foo()
{
    return 0;
}

B.cpp में हम फ़ंक्शन foo को कॉल करना चाहते हैं, इसलिए हम जोड़ते हैं

void foo();

फ़ंक्शन foo () घोषित करने के लिए, और इसे किसी अन्य फ़ंक्शन बॉडी में कॉल करें, bar() कहें:

void bar()
{
    foo();
}

अब जब आप इस कोड को बनाते हैं तो आपको एक एलएनके201 9 त्रुटि मिल जाएगी जो शिकायत करती है कि foo एक अनसुलझे प्रतीक है। इस मामले में, हम जानते हैं कि foo () की परिभाषा a.cpp में है, लेकिन जिसे हम कॉल कर रहे हैं उससे भिन्न (अलग-अलग वापसी मान)। यही वह मामला है जो परिभाषा मौजूद है।

  1. परिभाषा मौजूद नहीं है

अगर हम लाइब्रेरी में कुछ फ़ंक्शंस कॉल करना चाहते हैं, लेकिन आयात लाइब्रेरी को आपकी प्रोजेक्ट सेटिंग के अतिरिक्त निर्भरता सूची में शामिल नहीं किया गया है ( Project | Properties | Configuration Properties | Linker | Input | Additional Dependency )। अब लिंकर एलएनके201 9 की रिपोर्ट करेगा क्योंकि वर्तमान खोज क्षेत्र में परिभाषा मौजूद नहीं है।


A wrapper around GNU ld that doesn't support linker scripts

Some .so files are actually GNU ld linker scripts , eg libtbb.so file is an ASCII text file with this contents:

INPUT (libtbb.so.2)

Some more complex builds may not support this. For example, if you include -v in the compiler options, you can see that the mainwin gcc wrapper mwdip discards linker script command files in the verbose output list of libraries to link in. A simple work around is to replace the linker script input command file with a copy of the file instead (or a symlink), eg

cp libtbb.so.2 libtbb.so

Or you could replace the -l argument with the full path of the .so, eg instead of -ltbb do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2


Linked .lib file is associated to a .dll

मेरी भी यही समस्या थी। Say i have projects MyProject and TestProject. I had effectively linked the lib file for MyProject to the TestProject. However, this lib file was produced as the DLL for the MyProject was built. Also, I did not contain source code for all methods in the MyProject, but only access to the DLL's entry points.

To solve the issue, i built the MyProject as a LIB, and linked TestProject to this .lib file (i copy paste the generated .lib file into the TestProject folder). I can then build again MyProject as a DLL. It is compiling since the lib to which TestProject is linked does contain code for all methods in classes in MyProject.


एक "अपरिभाषित संदर्भ / अनसुलझा बाहरी प्रतीक" क्या है

मैं यह समझाने की कोशिश करूंगा कि "अनिर्धारित संदर्भ / अनसुलझा बाहरी प्रतीक" क्या है।

नोट: मैं जी ++ और लिनक्स का उपयोग करता हूं और इसके लिए सभी उदाहरण हैं

उदाहरण के लिए हमारे पास कुछ कोड है

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

ठीक है (कोई त्रुटि नहीं), क्योंकि हम केवल ऑब्जेक्ट फ़ाइल बनाते हैं, लिंकिंग अभी तक नहीं किया गया है। लिंक करने का प्रयास करें

$ 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 में बाइबल ग्लोबल है (स्थानीय था)

संपर्क

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

और इसे चलाओ

$ ./prog 
123456789

ठीक है, हम इसे हैक :)

इसलिए, नतीजतन - एक "अपरिभाषित संदर्भ / अनसुलझा बाहरी प्रतीक त्रुटि" तब होती है जब लिंकर ऑब्जेक्ट फ़ाइलों में वैश्विक प्रतीकों को नहीं ढूंढ सकता है।


विजुअल स्टूडियो NuGet पैकेज को नए टूलसेट संस्करण के लिए अपडेट करने की आवश्यकता है

मुझे बस विजुअल स्टूडियो 2013 के साथ libpng को जोड़ने का प्रयास करने में यह समस्या आई थी। समस्या यह है कि पैकेज फ़ाइल में केवल विजुअल स्टूडियो 2010 और 2012 के लिए पुस्तकालय थे।

सही समाधान यह है कि डेवलपर एक अद्यतन पैकेज जारी करता है और फिर अपग्रेड करता है, लेकिन यह VS2013 पुस्तकालय फ़ाइलों को इंगित करते हुए VS2013 के लिए अतिरिक्त सेटिंग में हैकिंग द्वारा मेरे लिए काम करता है।

मैंने packagename\build\native\packagename.targets और उस फ़ाइल के अंदर, सभी v110 खंडों की प्रतिलिपि बनाकर पैकेज (समाधान की निर्देशिका के अंदर packages फ़ोल्डर में) को संपादित किया। मैंने स्थिति फ़ील्ड में v110 से v110 को बदल दिया है, केवल v110 रूप में फ़ाइल नाम पथ छोड़ने के लिए बहुत सावधान रहना है। इसने 2012 के लिए लाइब्रेरी से लिंक करने के लिए विजुअल स्टूडियो 2013 को अनुमति दी, और इस मामले में, यह काम किया।





unresolved-external