अपरिभाषित संदर्भ / अनसुलझे बाहरी प्रतीक त्रुटियां क्या हैं? सामान्य कारण क्या हैं और उन्हें कैसे ठीक करें / रोकें?
खुद को संपादित / जोड़ने के लिए स्वतंत्र महसूस करें।
अपरिभाषित संदर्भ / अनसुलझे बाहरी प्रतीक त्रुटियां क्या हैं? सामान्य कारण क्या हैं और उन्हें कैसे ठीक करें / रोकें?
खुद को संपादित / जोड़ने के लिए स्वतंत्र महसूस करें।
जवाबों:
सी ++ प्रोग्राम का संकलन कई चरणों में होता है, जैसा कि 2.2 द्वारा निर्दिष्ट किया गया है (संदर्भ के लिए कीथ थॉम्पसन को क्रेडिट) :
अनुवाद के वाक्यविन्यास नियमों में पूर्ववर्तीता निम्नलिखित चरणों द्वारा निर्दिष्ट है [फुटनोट देखें] ।
- यदि आवश्यक हो, तो मूल स्रोत वर्ण सेट (एंड-ऑफ-लाइन संकेतक के लिए नए-पंक्ति वर्णों को लागू करने) के लिए भौतिक स्रोत फ़ाइल वर्णों को मैप किया जाता है। [कटाव]
- बैकस्लैश कैरेक्टर (\) के तुरंत बाद एक नई-लाइन कैरेक्टर को डिलीट करने का प्रत्येक उदाहरण, लॉजिकल सोर्स लाइनों को बनाने के लिए भौतिक स्रोत लाइनों को फैलाता है। [कटाव]
- स्रोत फ़ाइल को प्रीप्रोसेसिंग टोकन (2.5) और सफेद-अंतरिक्ष वर्णों के अनुक्रम (टिप्पणियों सहित) में विघटित किया जाता है। [कटाव]
- प्रीप्रोसेसिंग निर्देशों को निष्पादित किया जाता है, मैक्रो इनवोकेशन का विस्तार किया जाता है, और _Pragma यूनिरी ऑपरेटर एक्सप्रेशन निष्पादित किए जाते हैं। [कटाव]
- प्रत्येक स्रोत चरित्र एक चरित्र शाब्दिक या एक स्ट्रिंग शाब्दिक में सदस्य सेट करता है, साथ ही एक चरित्र शाब्दिक या गैर-कच्चे स्ट्रिंग शाब्दिक में प्रत्येक एस्केप अनुक्रम और सार्वभौमिक-चरित्र-नाम, निष्पादन वर्ण सेट के संबंधित सदस्य में परिवर्तित होता है; [कटाव]
- आसन्न स्ट्रिंग शाब्दिक टोकन समवर्ती होते हैं।
- टोकन को अलग करने वाले सफेद-अंतरिक्ष वर्ण अब महत्वपूर्ण नहीं हैं। प्रत्येक प्रीप्रोसेसिंग टोकन को टोकन में परिवर्तित किया जाता है। (2.7)। परिणामी टोकन को वाक्य रचना के रूप में क्रमबद्ध और शब्दार्थ रूप से विश्लेषित और अनुवादित किया जाता है। [कटाव]
- अनूदित अनुवाद इकाइयाँ और तात्कालिकता इकाइयाँ इस प्रकार हैं: [SNIP]
- सभी बाहरी निकाय संदर्भ हल किए गए हैं। वर्तमान अनुवाद में परिभाषित नहीं की गई संस्थाओं के लिए बाहरी संदर्भों को संतुष्ट करने के लिए लाइब्रेरी घटकों को जोड़ा जाता है। इस तरह के सभी अनुवादक आउटपुट को एक प्रोग्राम इमेज में एकत्र किया जाता है जिसमें निष्पादन निष्पादन के लिए आवश्यक जानकारी होती है। (जोर मेरा)
[फुटनोट] कार्यान्वयन को ऐसे व्यवहार करना चाहिए जैसे कि ये अलग-अलग चरण होते हैं, हालांकि व्यवहार में विभिन्न चरणों को एक साथ जोड़ दिया जा सकता है।
संकलन के इस अंतिम चरण के दौरान निर्दिष्ट त्रुटियां होती हैं, जिन्हें आमतौर पर लिंकिंग कहा जाता है। इसका मूल रूप से मतलब है कि आपने ऑब्जेक्ट फ़ाइलों या पुस्तकालयों में कार्यान्वयन फ़ाइलों का एक गुच्छा संकलित किया है और अब आप उन्हें एक साथ काम करना चाहते हैं।
कहते हैं कि आपने प्रतीक a
को परिभाषित किया है a.cpp
। अब, उस प्रतीक को b.cpp
घोषित किया और उसका उपयोग किया। लिंक करने से पहले, यह माना जाता है कि वह प्रतीक कहीं परिभाषित किया गया था , लेकिन यह अभी तक परवाह नहीं करता है कि कहां है। लिंकिंग चरण प्रतीक को खोजने और इसे सही तरीके से जोड़ने के लिए ज़िम्मेदार है b.cpp
(अच्छी तरह से, वास्तव में ऑब्जेक्ट या लाइब्रेरी जो इसका उपयोग करता है)।
यदि आप Microsoft Visual Studio का उपयोग कर रहे हैं, तो आप देखेंगे कि परियोजनाएँ .lib
फाइलें उत्पन्न करती हैं । इनमें निर्यात किए गए प्रतीकों की एक तालिका और आयातित प्रतीकों की एक तालिका है। आयात किए गए प्रतीकों को आपके द्वारा लिंक किए गए पुस्तकालयों के खिलाफ हल किया जाता है, और निर्यात किए गए प्रतीकों को उपयोग करने वाले पुस्तकालयों के लिए प्रदान किया जाता है.lib
(यदि कोई हो) ।
इसी तरह के तंत्र अन्य संकलक / प्लेटफार्मों के लिए मौजूद हैं।
सामान्य त्रुटि संदेश हैं error LNK2001
, error LNK1120
, error LNK2019
के लिए माइक्रोसॉफ्ट विजुअल स्टूडियो और 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 विज़ुअल स्टूडियो के साथ ऐसी ही त्रुटियां :
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
(Microsoft Visual Studio) का उपयोग करते समय .lib एक्सटेंशन को शामिल करना या न करनाUNICODE
परिभाषाएँvirtual
विध्वंसक को एक कार्यान्वयन की आवश्यकता होती है।एक विध्वंसक शुद्ध घोषित करने के बाद भी आपको इसे परिभाषित करने की आवश्यकता है (एक नियमित कार्य के विपरीत):
struct X
{
virtual ~X() = 0;
};
struct Y : X
{
~Y() {}
};
int main()
{
Y y;
}
//X::~X(){} //uncomment this line for successful definition
ऐसा इसलिए होता है क्योंकि बेस क्लास डिस्ट्रक्टर्स को कहा जाता है जब ऑब्जेक्ट को स्पष्ट रूप से नष्ट कर दिया जाता है, इसलिए एक परिभाषा की आवश्यकता होती है।
virtual
विधियों को या तो लागू किया जाना चाहिए या शुद्ध रूप में परिभाषित किया जाना चाहिए।यह 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()
शुद्ध घोषित करें :
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
डेटा सदस्यों के लिए वर्ग के अंदर आरंभीकरण की अनुमति देता है ।
आमतौर पर, प्रत्येक अनुवाद इकाई एक ऑब्जेक्ट फ़ाइल उत्पन्न करेगी जिसमें उस अनुवाद इकाई में परिभाषित प्रतीकों की परिभाषाएँ शामिल हैं। उन प्रतीकों का उपयोग करने के लिए, आपको उन ऑब्जेक्ट फ़ाइलों के खिलाफ लिंक करना होगा।
जीसीसी के तहत आप सभी ऑब्जेक्ट फ़ाइलों को निर्दिष्ट करेंगे जिन्हें कमांड लाइन में एक साथ जोड़ा जाना है, या कार्यान्वयन फ़ाइलों को एक साथ संकलित करना है।
g++ -o test objectFile1.o objectFile2.o -lLibraryName
libraryName
यहाँ, बस पुस्तकालय के नंगे नाम है प्लेटफ़ॉर्म-विशिष्ट शब्दों के बिना भी। इसलिए उदाहरण के तौर पर लिनक्स लाइब्रेरी की फाइलों को आमतौर पर कहा जाता है libfoo.so
लेकिन आप केवल लिखेंगे -lfoo
। विंडोज़ पर उसी फ़ाइल को बुलाया जा सकता है foo.lib
, लेकिन आप उसी तर्क का उपयोग करेंगे। आपको उस निर्देशिका को जोड़ना पड़ सकता है जहाँ उन फ़ाइलों का उपयोग करके पाया जा सकता है -L‹directory›
। एक जगह के बाद -l
या नहीं लिखना सुनिश्चित करें-L
।
के लिए XCode : -> लाइब्रेरी खोज पथ को जोड़ने - जोड़ें उपयोगकर्ता हैडर खोजें पथ> खींचें और परियोजना फ़ोल्डर में वास्तविक पुस्तकालय संदर्भ छोड़ देते हैं।
MSVS के तहत , किसी प्रोजेक्ट में जोड़ी गई फाइलें स्वचालित रूप से अपनी ऑब्जेक्ट फाइलों को एक साथ जोड़ देती हैं और एक lib
फाइल जेनरेट हो जाएगी (आम उपयोग में)। एक अलग प्रोजेक्ट में प्रतीकों का उपयोग करने के लिए, आपको lib
प्रोजेक्ट सेटिंग्स में फ़ाइलों को शामिल करना होगा। यह प्रोजेक्ट प्रॉपर्टीज के लिंकर सेक्शन में किया गया है Input -> Additional Dependencies
। ( lib
फ़ाइल का पथ जोड़ा जाना चाहिए Linker -> General -> Additional Library Directories
) जब किसी lib
फ़ाइल के साथ प्रदान की जाने वाली तृतीय-पक्ष लाइब्रेरी का उपयोग करते हैं , तो ऐसा करने में विफलता आमतौर पर त्रुटि का कारण बनती है।
यह भी हो सकता है कि आप फ़ाइल को संकलन में जोड़ना भूल जाएं, जिस स्थिति में ऑब्जेक्ट फ़ाइल उत्पन्न नहीं होगी। में जीसीसी आप आदेश पंक्ति में फ़ाइलें जोड़ने चाहते हैं। में MSVS परियोजना के लिए फ़ाइल जोड़ने यह यह स्वचालित रूप से संकलित (यद्यपि फ़ाइलों कर सकते हैं, मैन्युअल रूप से, व्यक्तिगत रूप से निर्माण से बाहर रखा जाना) कर देगा।
विंडोज प्रोग्रामिंग में, कहानी-कहानी का संकेत है कि आपने एक आवश्यक पुस्तकालय लिंक नहीं किया है कि अनसुलझे प्रतीक का नाम इसके साथ शुरू होता है __imp_
। प्रलेखन में फ़ंक्शन का नाम देखें, और यह कहना चाहिए कि आपको किस लाइब्रेरी का उपयोग करने की आवश्यकता है। उदाहरण के लिए, MSDN प्रत्येक फ़ंक्शन के निचले भाग में "लाइब्रेरी" नामक एक बॉक्स में जानकारी रखता है।
gcc main.c
इसके बजाय की सामान्य गलती को कवर कर सकते हैं gcc main.c other.c
(जो शुरुआती अक्सर अपनी परियोजनाओं को बनाने से पहले ऐसा करते हैं। जैसे कि .o फाइलें)।
एक विशिष्ट चर घोषणा है
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-क्वालिफायर बेमेल हो सकते हैं:
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)
बेमेल के अन्य उदाहरणों में शामिल हैं
कंपाइलर से त्रुटि संदेश आपको अक्सर उस चर या फ़ंक्शन की पूर्ण घोषणा देगा जो घोषित किया गया था लेकिन कभी भी परिभाषित नहीं किया गया था। आपके द्वारा प्रदान की गई परिभाषा से इसकी तुलना करें। सुनिश्चित करें कि हर विवरण मेल खाता हो।
#includes
नहीं जोड़ा जाता है, यह भी लापता परिभाषाओं की श्रेणी में आता है।
यदि पुस्तकालय एक-दूसरे पर निर्भर करते हैं, तो पुस्तकालय जिस क्रम से जुड़े होते हैं, वह क्रम चलता है। सामान्य तौर पर, यदि लाइब्रेरी लाइब्रेरी 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
तो फिर से दोहराने के लिए, आदेश बात करता है!
"अपरिभाषित संदर्भ / अनसुलझे बाहरी प्रतीक" क्या है
मैं यह समझाने की कोशिश करूंगा कि एक "अपरिभाषित संदर्भ / अनसुलझे बाहरी प्रतीक" क्या है।
नोट: मैं g ++ और लिनक्स का उपयोग करता हूं और इसके लिए सभी उदाहरण हैं
उदाहरण के लिए हमारे पास कुछ कोड हैं
// 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 में 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 ++ प्रोग्राम में उपयोग करने का प्रयास करते हैं।
यदि एक संपूर्ण लाइब्रेरी को हेडर फ़ाइल में शामिल किया गया है (और सी कोड के रूप में संकलित किया गया था); शामिल करने की आवश्यकता इस प्रकार होगी;
extern "C" {
#include "cheader.h"
}
#ifdef __cplusplus [\n] extern"C" { [\n] #endif
और #ifdef __cplusplus [\n] } [\n] #endif
( [\n]
वास्तविक कैरिज रिटर्न होने के नाते ) संरक्षित करना है, लेकिन मैं इसे टिप्पणी में ठीक से नहीं लिख सकता हूं।
extern "C" { #include <myCppHeader.h> }
।
यदि अन्य सभी विफल रहता है, तो recompile।
मैं हाल ही में दृश्य स्टूडियो 2012 में एक अनसुलझे बाहरी त्रुटि से छुटकारा पाने में सक्षम था, जो कि आपत्तिजनक फ़ाइल को पुनः प्राप्त कर रहा था। जब मैंने दोबारा बनाया, तो त्रुटि दूर हो गई।
यह आमतौर पर तब होता है जब दो (या अधिक) पुस्तकालयों में चक्रीय निर्भरता होती है। लाइब्रेरी ए बीलिब में प्रतीकों का उपयोग करने का प्रयास करती है और पुस्तकालय बी एलिब से प्रतीकों का उपयोग करने का प्रयास करता है। के साथ शुरू करने के लिए मौजूद नहीं है। जब आप A को संकलित करने का प्रयास करते हैं, तो लिंक चरण विफल हो जाएगा क्योंकि यह B.lib नहीं ढूँढ सकता है। A.lib उत्पन्न होगा, लेकिन कोई dll नहीं। आप फिर बी को संकलित करते हैं, जो बीलिब को सफल और उत्पन्न करेगा। ए-री का संकलन अब काम करेगा क्योंकि बी.लिब अब मिल गया है।
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 ) )।
आप आयात / निर्यात वर्गों का अनुकरण कर सकते हैं:
class DLLIMPEXP X
{
};
visibility
और विंडोज की .def
फाइलों का उल्लेख होना चाहिए , क्योंकि ये प्रतीक नाम और उपस्थिति को भी प्रभावित करते हैं।
.def
उम्र में फ़ाइलों का उपयोग नहीं किया है। एक उत्तर जोड़ने या इसे संपादित करने के लिए स्वतंत्र महसूस करें।
यह सबसे भ्रामक त्रुटि संदेशों में से एक है जो प्रत्येक VC ++ प्रोग्रामर ने समय और फिर से देखा है। पहले चीजों को स्पष्ट करते हैं।
A. प्रतीक क्या है? संक्षेप में, एक प्रतीक एक नाम है। यह एक चर नाम, एक फ़ंक्शन नाम, एक क्लास नाम, एक टाइपडेफ़ नाम या उन नामों और संकेतों को छोड़कर कुछ भी हो सकता है जो C ++ भाषा के हैं। यह एक निर्भरता पुस्तकालय (किसी अन्य उपयोगकर्ता द्वारा परिभाषित) द्वारा परिभाषित या पेश किया गया उपयोगकर्ता है।
B. बाहरी क्या है?
VC ++ में, प्रत्येक स्रोत फ़ाइल (.cpp, .c, आदि) को अनुवाद इकाई माना जाता है, संकलक एक समय में एक इकाई संकलित करता है, और वर्तमान अनुवाद इकाई के लिए एक ऑब्जेक्ट फ़ाइल (.obj) उत्पन्न करता है। (ध्यान दें कि शामिल की गई प्रत्येक हेडर फ़ाइल इस स्रोत फ़ाइल को प्रीप्रोसेस किया जाएगा और इस अनुवाद इकाई के हिस्से के रूप में माना जाएगा) अनुवाद इकाई के भीतर सब कुछ आंतरिक माना जाता है, बाकी सब कुछ बाहरी माना जाता है। C ++ में, आप जैसे कीवर्ड का उपयोग करके एक बाहरी प्रतीक को संदर्भित कर सकते हैं extern
,__declspec (dllimport)
और इसी तरह।
C. "संकल्प" क्या है? रिज़ॉल्यूशन एक लिंकिंग-टाइम टर्म है। लिंकिंग-टाइम में, लिंकर ऑब्जेक्ट फ़ाइलों में हर प्रतीक के लिए बाहरी परिभाषा खोजने का प्रयास करता है जो आंतरिक रूप से इसकी परिभाषा नहीं पा सकते हैं। इस खोज प्रक्रिया का दायरा इसमें शामिल है:
इस खोज प्रक्रिया को संकल्प कहा जाता है।
डी। आखिर, अनारक्षित बाहरी प्रतीक क्यों? यदि लिंकर बाहरी प्रतीक के लिए बाहरी परिभाषा नहीं पा सकता है जिसकी आंतरिक रूप से कोई परिभाषा नहीं है, तो यह एक अनारक्षित बाहरी प्रतीक त्रुटि की रिपोर्ट करता है।
ई। LNK2019 के संभावित कारण : अनारक्षित बाहरी प्रतीक त्रुटि। हम पहले से ही जानते हैं कि यह त्रुटि बाहरी चिह्न की परिभाषा को खोजने में विफल लिंकर के कारण है, संभावित कारणों को निम्नानुसार हल किया जा सकता है:
उदाहरण के लिए, यदि हमारे पास एक फ़ंक्शन है जिसे फू कहा जाता है। a.cpp में परिभाषित किया गया है:
int foo()
{
return 0;
}
B.cpp में हम फ़ंक्शन फू को कॉल करना चाहते हैं, इसलिए हम जोड़ते हैं
void foo();
घोषणा समारोह foo (), और एक अन्य समारोह शरीर में इसे कहते हैं, का कहना है bar()
:
void bar()
{
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
या समान 'असामान्य' main()
प्रविष्टि बिंदु संदर्भ (विशेषकर के लिए)दृश्य स्टूडियो)।
आप अपनी वास्तविक IDE के साथ सही प्रोजेक्ट प्रकार चुनने में चूक गए होंगे। आईडीई आमतौर पर उपयोग int main(int argc, char** argv);
किए जाने वाले हस्ताक्षर के बजाय इस तरह के प्रवेश बिंदु फ़ंक्शन (उदाहरण के लिए ऊपर दिए गए अनुपलब्ध संदर्भ में निर्दिष्ट) के लिए उदाहरण के लिए विंडोज एप्लीकेशन परियोजनाओं को बांधना चाह सकता है ।
यदि आपका आईडीई प्लेन कंसोल प्रोजेक्ट्स का समर्थन करता है, तो आप विंडोज़ एप्लिकेशन प्रोजेक्ट के बजाय इस प्रोजेक्ट प्रकार को चुनना चाह सकते हैं।
यहाँ case1 और case2 एक से अधिक विस्तार में संभाला असली दुनिया समस्या।
विजुअल स्टूडियो नुगेट पैकेज को नए टूलसेट संस्करण के लिए अद्यतन करने की आवश्यकता है
मुझे सिर्फ यह समस्या Visual Studio 2013 के साथ libpng को जोड़ने की कोशिश कर रही थी। समस्या यह है कि पैकेज फ़ाइल में केवल Visual Studio 2010 और 2012 के लिए लाइब्रेरी थी।
सही समाधान यह है कि डेवलपर को एक अद्यतन पैकेज जारी करने और फिर अपग्रेड करने की उम्मीद है, लेकिन इसने मेरे लिए VS2013 के लिए एक अतिरिक्त सेटिंग में हैक करके, VS2012 लाइब्रेरी फ़ाइलों की ओर इशारा करते हुए काम किया।
मैंने सभी अनुभागों की नकल करके उस फ़ाइल packages
को ढूंढकर packagename\build\native\packagename.targets
और उसके अंदर ( समाधान की निर्देशिका के अंदर फ़ोल्डर में) पैकेज संपादित किया v110
। मैं बदल v110
करने v120
में हालत क्षेत्रों केवल सभी के रूप में फ़ाइल नाम रास्तों को छोड़ने के लिए बहुत सावधान किया जा रहा है v110
। इसने दृश्य स्टूडियो 2013 को 2012 के लिए पुस्तकालयों से जोड़ने की अनुमति दी, और इस मामले में, इसने काम किया।
मान लीजिए कि आपके पास c ++ में लिखी गई एक बड़ी परियोजना है, जिसमें एक हजार .cpp फाइलें और एक .h फाइलें हैं। और मान लीजिए कि यह परियोजना दस स्थैतिक पुस्तकालयों पर भी निर्भर करती है। मान लीजिए कि हम विंडोज पर हैं और हम विजुअल स्टूडियो 20xx में अपना प्रोजेक्ट बनाते हैं। जब आप संपूर्ण समाधान संकलित करने के लिए Ctrl + F7 Visual Studio दबाते हैं (मान लें कि हमारे पास समाधान में सिर्फ एक परियोजना है)
संकलन का अर्थ क्या है?
संकलन का दूसरा चरण लिंकर द्वारा किया जाता है। लिकर को सभी ऑब्जेक्ट फ़ाइल को मर्ज करना चाहिए और अंत में आउटपुट का निर्माण करना चाहिए (जो एक निष्पादन योग्य या पुस्तकालय हो सकता है)
एक परियोजना को जोड़ने में कदम
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
अवलोकन
इस तरह की त्रुटि को कैसे हल करें
संकलक समय त्रुटि:
लिंकर टाइम एरर
#pragma once
संकलक को एक हेडर शामिल न करने देने के लिए उपयोग करें यदि यह पहले से ही वर्तमान .cpp में शामिल है जो संकलित हैंमुझे हाल ही में यह समस्या हुई थी, और यह दृश्य स्टूडियो एक्सप्रेस 2013 में एक बग था । मुझे प्रोजेक्ट से एक स्रोत फ़ाइल को निकालना था और बग को दूर करने के लिए इसे फिर से जोड़ना था।
यदि आप मानते हैं कि यह कंपाइलर / IDE में बग हो सकता है, तो प्रयास करने के चरण:
अधिकांश आधुनिक लिंकर्स में एक क्रिया विकल्प शामिल होता है जो अलग-अलग डिग्री तक प्रिंट करता है;
Gcc और clang के लिए; आप आम तौर पर जोड़ना होगा -v -Wl,--verbose
या -v -Wl,-v
कमांड लाइन करने के लिए। अधिक विवरण यहां मिल सकता है;
MSVC के लिए, /VERBOSE
(विशेष रूप से /VERBOSE:LIB
) लिंक कमांड लाइन में जोड़ा जाता है।
/VERBOSE
लिंकर विकल्प पर MSDN पृष्ठ ।लिंक्ड .लिब फ़ाइल .dll से संबंधित है
मेरी भी यही समस्या थी। मान लीजिए कि मेरे पास MyProject और TestProject प्रोजेक्ट हैं। मैंने MyProject के लिए lib फाइल को TestProject में प्रभावी रूप से जोड़ा था। हालाँकि, इस lib फ़ाइल को MyLject के लिए DLL के रूप में निर्मित किया गया था। इसके अलावा, मेरे पास MyProject में सभी तरीकों के लिए स्रोत कोड नहीं था, लेकिन केवल DLL के प्रवेश बिंदुओं तक पहुंच है।
समस्या को हल करने के लिए, मैंने MyProject को LIB के रूप में बनाया, और TestProject को इस .lib फ़ाइल से जोड़ा (i जनरेट हुई .lib फ़ाइल को TestProject फ़ोल्डर में पेस्ट करें)। मैं फिर एक DLL के रूप में फिर से MyProject का निर्माण कर सकता हूं। यह संकलित किया जा रहा है क्योंकि टेस्टप्रोजेक्ट से जुड़ी लीब में माईप्रोजेक्ट की कक्षाओं में सभी विधियों के लिए कोड होता है।
चूँकि लोगों को इस प्रश्न के लिए निर्देशित किया जा रहा है जब यह लिंकर त्रुटियों की बात आती है मैं इसे यहाँ जोड़ने जा रहा हूँ।
जीसीसी 5.2.0 के साथ लिंकर त्रुटियों का एक संभावित कारण यह है कि अब एक नया libstdc ++ लाइब्रेरी ABI डिफ़ॉल्ट रूप से चुना गया है।
यदि आपको std :: __ cxx11 नाम स्थान या टैग [abi: cxx11] में प्रतीकों के अपरिभाषित संदर्भों के बारे में लिंकर त्रुटियां मिलती हैं, तो यह संभवतः इंगित करता है कि आप _GLIBCXX_USE_CXX11_ABI के लिए विभिन्न मानों के साथ संकलित ऑब्जेक्ट फ़ाइलों को एक साथ जोड़ने का प्रयास कर रहे हैं। मैक्रो। यह आमतौर पर तीसरे पक्ष के पुस्तकालय से लिंक करते समय होता है जिसे जीसीसी के पुराने संस्करण के साथ संकलित किया गया था। अगर नए ABI के साथ थर्ड-पार्टी लाइब्रेरी का पुनर्निर्माण नहीं किया जा सकता है, तो आपको पुराने ABI के साथ अपने कोड को फिर से जोड़ना होगा।
इसलिए अगर आपको 5.1.0 के बाद जीसीसी में स्विच करते समय अचानक लिंकर की त्रुटियां आती हैं, तो यह जांचने की चीज होगी।
GNU ld के आसपास एक आवरण जो लिंकर स्क्रिप्ट का समर्थन नहीं करता है
कुछ .so फाइलें वास्तव में GNU ld linker स्क्रिप्ट हैं , जैसे libtbb.so फाइल इस के लिए एक ASCII टेक्स्ट फाइल है:
INPUT (libtbb.so.2)
कुछ और जटिल बिल्ड इसका समर्थन नहीं कर सकते हैं। उदाहरण के लिए, यदि आप संकलक विकल्पों में -v शामिल करते हैं, तो आप देख सकते हैं कि लिंक करने के लिए पुस्तकालयों की वर्बोज़ आउटपुट सूची में मेनविन gcc रैपर mwdip डिस्क्स लिंकर स्क्रिप्ट कमांड फ़ाइलें। चारों ओर एक साधारण काम लिंकर स्क्रिप्ट इनपुट कमांड को बदलने के लिए है। इसके बजाय फ़ाइल की एक प्रति (या एक सिमलिंक) के साथ फाइल करें, जैसे
cp libtbb.so.2 libtbb.so
या -l तर्क को .so के पूर्ण पथ से प्रतिस्थापित कर सकते हैं, जैसे कि -ltbb
do के बजाय/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
libfoo
निर्भर करता है libbar
, तो आपका लिंकेज सही तरीके libfoo
से पहले रखता है libbar
।undefined reference to
कुछ त्रुटियों के साथ विफल हो जाता है ।#include
जाते हैं और वास्तव में उन पुस्तकालयों में परिभाषित होते हैं जिन्हें आप लिंक कर रहे हैं।उदाहरण सी में हैं। वे समान रूप से अच्छी तरह से सी ++ हो सकते हैं
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
एक ही परिणाम यदि आप एक चरण में संकलित और लिंक करते हैं, जैसे:
$ 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
और उदाहरण 2 में एक भिन्नता शामिल है pkg-config
:
$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
ऑब्जेक्ट फ़ाइलों और पुस्तकालयों के अनुक्रम में आप अपने प्रोग्राम को बनाने के लिए लिंक करना चाहते हैं, आप ऑब्जेक्ट फ़ाइलों से पहले पुस्तकालयों को रख रहे हैं जो उन्हें संदर्भित करते हैं। आपको इसके बाद पुस्तकालयों को रखने की आवश्यकता है उन ऑब्जेक्ट फ़ाइलों के जो उन्हें संदर्भित करते हैं।
लिंक उदाहरण 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
पढ़ना यहाँ से वैकल्पिक है ।
डिफ़ॉल्ट रूप से, आपके डिस्ट्रो पर जीसीसी द्वारा उत्पन्न एक लिंकेज कमांड, कमांडलाइन अनुक्रम में बाएं से दाएं लिंकेज में फाइलों का उपभोग करता है। जब यह पता चलता है कि एक फ़ाइल किसी चीज़ को संदर्भित करती है और इसके लिए परिभाषा नहीं होती है, तो आगे दाईं ओर फ़ाइलों में परिभाषा की खोज की जाएगी। यदि यह अंततः एक परिभाषा पाता है, तो संदर्भ हल हो जाता है। यदि कोई संदर्भ अंत में अनसुलझे रहते हैं, तो लिंकेज विफल हो जाता है: लिंकर पीछे की ओर नहीं खोजता है।
सबसे पहले, उदाहरण 1 , स्थैतिक पुस्तकालय के साथmy_lib.a
एक स्थिर पुस्तकालय ऑब्जेक्ट फ़ाइलों का एक अनुक्रमित संग्रह है। जब लिंकर -lmy_lib
लिंकेज अनुक्रम में पाता है और यह पता लगाता है कि यह स्थैतिक पुस्तकालय को संदर्भित करता है ./libmy_lib.a
, तो यह जानना चाहता है कि क्या आपके प्रोग्राम को ऑब्जेक्ट फ़ाइलों में से किसी की आवश्यकता है libmy_lib.a
।
libmy_lib.a
अर्थात् में केवल ऑब्जेक्ट फ़ाइल है , my_lib.o
और वहाँ केवल एक चीज में परिभाषित है my_lib.o
, अर्थात् कार्य hw
।
लिंकर यह तय करेगा कि आपके कार्यक्रम की जरूरत my_lib.o
है और केवल अगर यह पहले से ही जानता है कि आपके कार्यक्रम को संदर्भित करता है hw
, तो एक या एक से अधिक ऑब्जेक्ट फ़ाइलों में यह पहले से ही प्रोग्राम में जोड़ा गया है, और यह कि पहले से ही जोड़ा गया ऑब्जेक्ट फ़ाइलों में से कोई भी शामिल नहीं है के लिए परिभाषा hw
।
यदि यह सच है, तो लिंकर my_lib.o
पुस्तकालय से एक प्रति निकालेगा और आपके कार्यक्रम में जोड़ देगा। फिर, आपके कार्यक्रम में एक परिभाषा है hw
, इसलिए इसके संदर्भ हलhw
किए जाते हैं ।
जब आप प्रोग्राम को लिंक करने का प्रयास करते हैं जैसे:
$ gcc -o eg1 -L. -lmy_lib eg1.o
लिंकर प्रोग्राम को देखता है जब यह जोड़ा नहीं है
। क्योंकि उस बिंदु पर, यह नहीं देखा है । आपका कार्यक्रम अभी तक के सभी संदर्भ पड़ता है नहीं : यह अभी तक कोई भी संदर्भ नहीं है सब पर है, क्योंकि सभी संदर्भों को यह बनाता है में हैं ।eg1.o
-lmy_lib
eg1.o
hw
eg1.o
इसलिए लिंकर my_lib.o
कार्यक्रम में शामिल नहीं होता है और इसका कोई और उपयोग नहीं है libmy_lib.a
।
अगला, इसे पाता है eg1.o
, और इसे प्रोग्राम होने के लिए जोड़ता है। लिंकेज अनुक्रम में एक ऑब्जेक्ट फ़ाइल को हमेशा प्रोग्राम में जोड़ा जाता है। अब, कार्यक्रम एक संदर्भ बनाता है hw
, और इसमें परिभाषा नहीं होती है hw
; लेकिन लिंकेज अनुक्रम में कुछ भी नहीं बचा है जो लापता परिभाषा प्रदान कर सकता है। संदर्भ अनसुलझेhw
समाप्त होता है , और लिंकेज विफल हो जाता है।
साझा पुस्तकालय के साथ दूसरा, उदाहरण 2libz
एक साझा लाइब्रेरी ऑब्जेक्ट फ़ाइलों या इसके जैसे कुछ का संग्रह नहीं है। यह एक प्रोग्राम की तरह बहुत अधिक है, जिसमें कोई main
फ़ंक्शन नहीं है और इसके बजाय यह परिभाषित करने वाले कई अन्य प्रतीकों को उजागर करता है, ताकि अन्य प्रोग्राम रनटाइम के दौरान उनका उपयोग कर सकें।
कई Linux distros आज उनके जीसीसी toolchain ताकि उसके भाषा ड्राइवरों (कॉन्फ़िगर gcc
, g++
, gfortran
आदि) प्रणाली लिंकर (हिदायत ld
एक पर लिंक साझा पुस्तकालयों के लिए) के रूप में जरूरी आधार। आपको उन विकृतियों में से एक मिला है।
इसका मतलब यह है कि जब लिंकर -lz
लिंकेज अनुक्रम में पाता है, और यह पता लगाता है कि यह साझा लाइब्रेरी (कहता है) को संदर्भित करता है /usr/lib/x86_64-linux-gnu/libz.so
, तो यह जानना चाहता है कि क्या आपके प्रोग्राम में शामिल किए गए किसी भी संदर्भ को अभी तक परिभाषित नहीं किया गया है, इसकी परिभाषाएं हैं द्वारा निर्यात किया गयाlibz
अगर यह सच है, तो लिंकर बाहर किसी भी हिस्सा को कॉपी नहीं करेगा libz
और उन्हें आपके प्रोग्राम में जोड़ देगा; इसके बजाय, यह सिर्फ आपके प्रोग्राम के कोड को डॉक्टर करेगा ताकि: -
रनटाइम के दौरान, सिस्टम प्रोग्राम लोडर libz
आपके प्रोग्राम की एक ही प्रक्रिया में एक कॉपी लोड करेगा जब भी यह आपके प्रोग्राम की एक कॉपी लोड करता है, इसे चलाने के लिए।
रनटाइम के दौरान, जब भी आपका प्रोग्राम किसी ऐसी चीज़ को संदर्भित करता है, जिसे उस परिभाषित किया जाता है
libz
, तो संदर्भ libz
उसी प्रक्रिया की कॉपी द्वारा निर्यात की गई परिभाषा का उपयोग करता है।
आपका कार्यक्रम केवल एक चीज को संदर्भित करना चाहता है जिसकी एक परिभाषा है libz
जिसका निर्यात किया जाता है , अर्थात् फ़ंक्शन zlibVersion
, जिसे केवल एक बार, में संदर्भित किया जाता है eg2.c
। यदि लिंकर आपके कार्यक्रम में उस संदर्भ को जोड़ता है, और फिर द्वारा निर्यात की गई परिभाषा पाता है libz
, तो संदर्भ हल हो जाता है
लेकिन जब आप प्रोग्राम को लिंक करने का प्रयास करते हैं जैसे:
gcc -o eg2 -lz eg2.o
घटनाओं का क्रम ठीक उसी तरह से गलत है जैसे उदाहरण 1। जिस बिंदु पर जब लिंकर को पता चलता है -lz
, तो कार्यक्रम में किसी भी चीज का कोई संदर्भ नहीं होता है : वे सभी अंदर हैं eg2.o
, जो अभी तक नहीं देखा गया है। तो लिंकर यह तय करता है कि इसका कोई उपयोग नहीं है libz
। जब यह पहुंचता है eg2.o
, तो इसे प्रोग्राम में जोड़ता है, और फिर इसका अपरिभाषित संदर्भ होता है zlibVersion
, लिंकेज अनुक्रम समाप्त हो जाता है; वह संदर्भ अनसुलझा है, और लिंकेज विफल है।
अंत में, pkg-config
उदाहरण 2 की भिन्नता का अब स्पष्ट स्पष्टीकरण है। शेल-विस्तार के बाद:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
हो जाता है:
gcc -o eg2 -lz eg2.o
जो सिर्फ 2 उदाहरण फिर से है।
लिंकेज:
gcc -o eg2 -lz eg2.o
तुम्हारे लिए ठीक काम करता है!
(या: लिंकेज ने आपके लिए ठीक काम किया, कहते हैं, फेडोरा 23, लेकिन उबंटू 16.04 पर विफल रहता है)
ऐसा इसलिए है क्योंकि डिस्ट्रॉ जिस पर लिंकेज काम करता है, उनमें से एक है जो अपने जीसीसी टूलकिन को साझा पुस्तकालयों को आवश्यकतानुसार जोड़ने के लिए कॉन्फ़िगर नहीं करता है ।
दिन में, यूनिक्स जैसी प्रणालियों के लिए विभिन्न नियमों द्वारा स्थिर और साझा पुस्तकालयों को जोड़ना सामान्य था। एक सीक्वेंस सीक्वेंस में स्टेटिक लाइब्रेरीज़ को उदाहरण 1 में बताए गए आवश्यक आधार पर लिंक किया गया था , लेकिन साझा लाइब्रेरी को बिना शर्त लिंक किया गया था।
यह व्यवहार लिंकटाइम पर किफायती है क्योंकि लिंक करने वाले को यह विचार करने की आवश्यकता नहीं है कि प्रोग्राम द्वारा साझा लाइब्रेरी की आवश्यकता है: यदि यह एक साझा लाइब्रेरी है, तो इसे लिंक करें। और अधिकांश लिंकेज में अधिकांश लाइब्रेरी साझा लाइब्रेरी हैं। लेकिन इसके नुकसान भी हैं: -
यह रनटाइम में अनौपचारिक है , क्योंकि यह साझा पुस्तकालयों को एक कार्यक्रम के साथ लोड किए जाने का कारण बन सकता है, भले ही उनकी आवश्यकता न हो।
स्थिर और साझा पुस्तकालयों के लिए अलग-अलग लिंकेज नियम अनुभवहीन प्रोग्रामर को भ्रमित कर सकते हैं, जो यह नहीं जान सकते हैं कि -lfoo
उनके लिंकेज में /some/where/libfoo.a
या तो समाधान करने जा रहे हैं या नहीं /some/where/libfoo.so
, और वैसे भी साझा और स्थिर पुस्तकालयों के बीच अंतर को समझ नहीं सकते हैं।
इस व्यापार बंद के कारण आज विद्वतापूर्ण स्थिति पैदा हो गई है। कुछ पुस्तकालयों ने साझा पुस्तकालयों के लिए अपने जीसीसी लिंकेज नियमों को बदल दिया है ताकि सभी पुस्तकालयों के लिए आवश्यक सिद्धांत लागू हो। कुछ डिस्ट्रॉर्स पुराने तरीके से अटक गए हैं।
अगर मैं बस:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
निश्चित रूप से जीसीसी को eg1.c
पहले संकलित करना है , और फिर परिणामी ऑब्जेक्ट फ़ाइल के साथ लिंक करना है libmy_lib.a
। तो यह कैसे पता नहीं चल सकता है कि लिंक करते समय ऑब्जेक्ट फ़ाइल की आवश्यकता है?
क्योंकि एकल कमांड के साथ संकलन और लिंक करना लिंकेज अनुक्रम के क्रम को नहीं बदलता है।
जब आप ऊपर कमांड चलाते हैं, gcc
तो यह पता लगाते हैं कि आप संकलन + लिंकेज चाहते हैं। तो पर्दे के पीछे, यह एक संकलन कमांड उत्पन्न करता है, और इसे चलाता है, फिर एक लिंकेज कमांड उत्पन्न करता है, और इसे चलाता है, जैसे कि आपने दो कमांड चलाए थे:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
तो कड़ी में विफल रहता है अगर आप बस के रूप में यह होता है है उन दो आदेशों को चलाने के। विफलता में आपको केवल इतना ही अंतर दिखाई देता है कि जीसीसी ने संकलन + लिंक मामले में एक अस्थायी ऑब्जेक्ट फ़ाइल उत्पन्न की है, क्योंकि आप इसे उपयोग करने के लिए नहीं कह रहे हैं eg1.o
। हम देखते हैं:
/tmp/ccQk1tvs.o: In function `main'
के बजाय:
eg1.o: In function `main':
जिस क्रम में अन्योन्याश्रित लिंक्ड लाइब्रेरी निर्दिष्ट की गई है वह गलत है
गलत क्रम में अन्योन्याश्रित पुस्तकालयों लाना सिर्फ एक तरीका है जिसमें आप फ़ाइलों को प्राप्त कर सकते हैं की जरूरत है जो फ़ाइलें से संबंध में बाद में आने वाले चीजों की परिभाषा प्रदान परिभाषाएँ। ऑब्जेक्ट फाइल्स के सामने लाइब्रेरियों को रखना जो उन्हें संदर्भित करते हैं, वही गलती करने का एक और तरीका है।
एक दोस्त ऑपरेटर (या फ़ंक्शन) के साथ टेम्पलेट प्रकार के कोड स्निपेट को देखते हुए;
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 के नामस्थान को संलग्न करने वाले अंतरतम का सदस्य बन जाता है, लेकिन देखने के लिए सुलभ नहीं है (तर्क-निर्भर लुकअप को छोड़कर जो 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)
जब एक हेडर फ़ाइल और उससे जुड़ी साझा लाइब्रेरी (.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
। दोनों परियोजनाएं निर्भर करती हैं 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(...);
}
बूम! आपको एक लिंकर त्रुटि मिलती है और आपको पता नहीं है कि यह क्यों विफल हो रहा है। कारण यह है कि सामान्य पुस्तकालय में एक ही शामिल के विभिन्न संस्करणों का उपयोग किया जाता है common_math.h
(मैंने उदाहरण में इसे एक अलग पथ को शामिल करके स्पष्ट किया है, लेकिन यह हमेशा इतना स्पष्ट नहीं हो सकता है। हो सकता है कि संकलक सेटिंग में शामिल पथ अलग हो) ।
इस उदाहरण में ध्यान दें, लिंकर आपको बताएगा कि यह नहीं मिल सकता है draw()
, जब वास्तविकता में आप जानते हैं कि यह स्पष्ट रूप से पुस्तकालय द्वारा निर्यात किया जा रहा है। आप अपने सिर को खरोंचते हुए घंटों बिता सकते हैं कि क्या गलत हुआ। बात यह है, लिंकर एक अलग हस्ताक्षर देखता है क्योंकि पैरामीटर प्रकार थोड़ा अलग हैं। उदाहरण में, vec3
दोनों परियोजनाओं में एक अलग प्रकार है जहां तक संकलक का संबंध है। ऐसा इसलिए हो सकता है क्योंकि वे दो अलग-अलग फाइलों में शामिल हैं (शायद लाइब्रेरी के दो अलग-अलग संस्करणों से आने वाली फाइलें शामिल हैं)।
DUMPBIN आपका दोस्त है, यदि आप Visual Studio का उपयोग कर रहे हैं। मुझे यकीन है कि अन्य संकलक के पास अन्य समान उपकरण हैं।
प्रक्रिया इस प्रकार है:
[१] परियोजना से मेरा मतलब है स्रोत फ़ाइलों का एक सेट जो एक पुस्तकालय या एक निष्पादन योग्य बनाने के लिए एक साथ जुड़े हुए हैं।
EDIT 1: समझने के लिए आसान होने के लिए पहले खंड को फिर से लिखें। अगर कुछ और तय करना है तो कृपया मुझे नीचे कमेंट करके बताएं। धन्यवाद!
UNICODE
परिभाषाएँएक Windows UNICODE बिल्ड TCHAR
आदि के साथ परिभाषित किया जा रहा है wchar_t
आदि के रूप में बनाया गया है । जब के UNICODE
रूप में परिभाषित के साथ निर्माण के TCHAR
रूप में परिभाषित नहीं के रूप में char
आदि इन UNICODE
और _UNICODE
परिभाषित सभी " T
" स्ट्रिंग प्रकार को प्रभावित ; LPTSTR
, LPCTSTR
और उनके एल्क।
UNICODE
परिभाषित किए गए प्रोजेक्ट के साथ एक लाइब्रेरी का निर्माण करना और इसे एक ऐसी परियोजना में जोड़ने का प्रयास करना, जहां UNICODE
परिभाषित नहीं किया गया है, लिंकर त्रुटियों का परिणाम होगा क्योंकि परिभाषा में एक बेमेल होगा TCHAR
; char
बनाम wchar_t
।
त्रुटि में आमतौर पर एक फ़ंक्शन शामिल होता है char
या एक wchar_t
व्युत्पन्न प्रकार के साथ, ये शामिल हो सकते हैं std::basic_string<>
आदि। कोड में प्रभावित फ़ंक्शन के माध्यम से ब्राउज़ करते समय, अक्सर एक संदर्भ TCHAR
या std::basic_string<TCHAR>
आदि होगा। यह एक कथा-कहानी का संकेत है कि कोड मूल रूप से एक UNICODE और एक मल्टी-बाइट कैरेक्टर (या "संकीर्ण") दोनों के लिए बनाया गया था ।
इसे ठीक करने के लिए, UNICODE
(और _UNICODE
) की सुसंगत परिभाषा के साथ सभी आवश्यक पुस्तकालयों और परियोजनाओं का निर्माण करें ।
यह या तो के साथ किया जा सकता है;
#define UNICODE
#define _UNICODE
या परियोजना सेटिंग्स में;
प्रोजेक्ट गुण> सामान्य> प्रोजेक्ट डिफॉल्ट> चरित्र सेट
या कमांड लाइन पर;
/DUNICODE /D_UNICODE
विकल्प के रूप में अच्छी तरह से लागू है, अगर UNICODE का उपयोग करने का इरादा नहीं है, तो सुनिश्चित करें कि परिभाषित सेट नहीं हैं, और / या मल्टी-कैरेक्टर सेटिंग का उपयोग परियोजनाओं में किया जाता है और लगातार लागू किया जाता है।
"रिलीज़" और "डीबग" के बीच संगत होना न भूलें।
बिल्ड का "क्लीन" "डेड वुड" को हटा सकता है जिसे पिछले बिल्ड, फेल बिल्ड, अधूरे बिल्ड और अन्य बिल्ड सिस्टम से संबंधित बिल्ड मसलों के आसपास छोड़ दिया जा सकता है।
सामान्य तौर पर आईडीई या बिल्ड में "क्लीन" फ़ंक्शन के कुछ रूप शामिल होंगे, लेकिन यह सही तरीके से कॉन्फ़िगर नहीं किया जा सकता है (जैसे मैन्युअल मेकाइल में) या विफल हो सकता है (जैसे मध्यवर्ती या परिणामी बायनेरी केवल-पढ़ने के लिए हैं)।
एक बार "क्लीन" पूरा हो जाने के बाद, सत्यापित करें कि "क्लीन" सफल हो गया है और सभी जेनरेट की गई इंटरमीडिएट फाइल (उदाहरण के लिए स्वचालित मेफाइल) को सफलतापूर्वक हटा दिया गया है।
इस प्रक्रिया को अंतिम उपाय के रूप में देखा जा सकता है, लेकिन अक्सर यह एक अच्छा पहला कदम होता है ; खासकर यदि त्रुटि से संबंधित कोड हाल ही में जोड़ा गया है (या तो स्थानीय रूप से या स्रोत भंडार से)।
const
चर घोषणाओं / परिभाषाओं में "बाहरी" गुम (केवल C ++)C से आने वाले लोगों के लिए यह आश्चर्य की बात हो सकती है कि C ++ में वैश्विक const
चर में आंतरिक (या स्थिर) लिंकेज है। सी में यह मामला नहीं था, क्योंकि सभी वैश्विक चर स्पष्ट रूप से हैं 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
हालांकि यह कई स्वीकृत उत्तरों के साथ एक बहुत पुराना प्रश्न है, मैं एक अस्पष्ट हल करने के लिए साझा करना चाहूंगा "अपरिभाषित संदर्भ" त्रुटि ।
मैं संदर्भित करने के लिए एक उपनाम का उपयोग कर रहा था 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:
मुख्य पुस्तकालयों में प्रयुक्त विभिन्न पुस्तकालयों पर ध्यान दें और file.h. चूंकि main.cpp # "include'd file.h <के बाद" फाइल सिस्टम >, फाइल सिस्टम के संस्करण का उपयोग किया था सी ++ 17 एक । मैं निम्नलिखित आदेशों के साथ कार्यक्रम को संकलित करता था:
$ 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 लिंक जोड़ता है
इस तरह किसी भी समारोह file.o में निहित और main.o में प्रयोग किया जाता है कि आवश्यकpath_t
दे दी है "अपरिभाषित संदर्भ" त्रुटियों क्योंकि main.o करने के लिए भेजा std::filesystem::path
लेकिन file.o लिए std::experimental::filesystem::path
।
इसे ठीक करने के लिए मुझे बस <प्रायोगिक :: filesystem> को file.h में <filesystem> में बदलने की आवश्यकता थी ।
जीसीसी का डिफ़ॉल्ट व्यवहार यह है कि सभी प्रतीक दिखाई देते हैं। हालाँकि, जब अनुवाद इकाइयाँ विकल्प के साथ निर्मित होती हैं -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
आप नामों को गिराने nm
के विकल्प के साथ भी उपयोग कर सकते हैं -C
(यदि C ++ का उपयोग किया गया था)।
विंडोज-डीएलएस के समान, एक सार्वजनिक कार्यों को परिभाषित के साथ चिह्नित करेगा, उदाहरण के लिए DLL_PUBLIC
परिभाषित:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
जो मोटे तौर पर विंडोज / MSVC- संस्करण से मेल खाती है:
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
दृश्यता के बारे में अधिक जानकारी gcc विकि पर देखी जा सकती है।
जब एक अनुवाद इकाई को संकलित किया जाता -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'
उस स्थिति में, इसका मतलब है कि उपलब्ध प्रतीक आपके द्वारा संकलित किए जाने वाले की तुलना में एक अलग वास्तुकला के लिए हैं।
विज़ुअल स्टूडियो पर, यह गलत "प्लेटफ़ॉर्म" के कारण है, और आपको या तो उचित एक का चयन करना होगा या लाइब्रेरी का उचित संस्करण स्थापित करना होगा।
लिनक्स पर, यह गलत लाइब्रेरी फ़ोल्डर ( उदाहरण lib
के lib64
लिए उपयोग करने के बजाय ) के कारण हो सकता है ।
MacOS पर, एक ही फाइल में दोनों आर्किटेक्चर को शिपिंग करने का विकल्प है। यह हो सकता है कि लिंक को दोनों संस्करणों के होने की उम्मीद है, लेकिन केवल एक ही है। यह गलत lib
/ lib64
फ़ोल्डर के साथ एक मुद्दा भी हो सकता है जहां पुस्तकालय उठाया जाता है।