অপরিজ্ঞাত রেফারেন্স / অমীমাংসিত বাহ্যিক প্রতীক ত্রুটিগুলি কী কী? সাধারণ কারণগুলি কী কী এবং কীভাবে সেগুলি ঠিক করতে / প্রতিরোধ করবেন?
আপনার নিজের সম্পাদনা / যুক্ত করতে নির্দ্বিধায়
অপরিজ্ঞাত রেফারেন্স / অমীমাংসিত বাহ্যিক প্রতীক ত্রুটিগুলি কী কী? সাধারণ কারণগুলি কী কী এবং কীভাবে সেগুলি ঠিক করতে / প্রতিরোধ করবেন?
আপনার নিজের সম্পাদনা / যুক্ত করতে নির্দ্বিধায়
উত্তর:
একটি সি ++ প্রোগ্রাম সংকলনটি ২.২ (রেফারেন্সের জন্য কীথ থম্পসনের কাছে ক্রেডিট) দ্বারা নির্দিষ্ট করা হিসাবে কয়েকটি ধাপে সংঘটিত হয় :
অনুবাদের বাক্য গঠন নিয়মের মধ্যে প্রাধান্য নিম্নলিখিত ধাপগুলি দ্বারা নির্দিষ্ট করা হয়েছে [পাদটীকা দেখুন] ।
- শারীরিক উত্স ফাইলের অক্ষরগুলি ম্যাপ করা হয়, একটি প্রয়োগকরণ-সংজ্ঞায়িত পদ্ধতিতে, মূল উত্স অক্ষর সেটটিতে (প্রয়োজনীয় যদি প্রয়োজন হয় তবে লাইন সূচকগুলির জন্য নতুন-লাইন অক্ষর প্রবর্তন করা হবে)। [স্নিপ]
- তাত্ক্ষণিকভাবে একটি নতুন-রেখাযুক্ত অক্ষর (bac) এর ব্যাকস্ল্যাশ অক্ষরের প্রতিটি উদাহরণ মুছে ফেলা হবে, লজিকাল উত্স লাইনগুলি গঠনের জন্য দৈহিক উত্স লাইনগুলি বিভক্ত করে। [স্নিপ]
- উত্স ফাইলটি প্রোট্রোসেসিং টোকেনগুলি (2.5) এবং সাদা-স্থানের অক্ষরের ক্রম (মন্তব্য সহ )গুলিতে বিভক্ত হয়েছে। [স্নিপ]
- প্রাক-প্রসেসিং নির্দেশিকা কার্যকর করা হয়, ম্যাক্রো আমন্ত্রণগুলি প্রসারিত করা হয়, এবং_প্রেগমা অ্যানারি অপারেটর এক্সপ্রেশন কার্যকর করা হয়। [স্নিপ]
- প্রতিটি উত্স অক্ষর অক্ষর অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষরে অক্ষর অক্ষর অক্ষর অক্ষর অক্ষর অক্ষর অক্ষর অক্ষর অক্ষর অক্ষরে অক্ষর রূপান্তরিত হয়; [স্নিপ]
- সংলগ্ন স্ট্রিং আক্ষরিক টোকেন সংক্ষিপ্ত হয়।
- টোকেনগুলি পৃথক করে সাদা-স্থানের অক্ষরগুলি আর তাত্পর্যপূর্ণ নয়। প্রতিটি প্রাক-প্রসেসিং টোকন টোকেনে রূপান্তরিত হয়। (2.7)। ফলস্বরূপ টোকেনগুলি সিনট্যাক্টিকাল এবং শব্দার্থবিজ্ঞানের সাথে বিশ্লেষণ করা হয় এবং অনুবাদ ইউনিট হিসাবে অনুবাদ করা হয়। [স্নিপ]
- অনুবাদকৃত অনুবাদ ইউনিট এবং ইনস্ট্যান্টেশন ইউনিটগুলি নীচে একত্রিত হয়েছে: [এসএনআইপি]
- সমস্ত বাহ্যিক সত্তার রেফারেন্সগুলি সমাধান করা হয়েছে। লাইব্রেরির উপাদানগুলি বর্তমান অনুবাদে সংজ্ঞায়িত নয় এমন সত্তাগুলির বাহ্যিক রেফারেন্সগুলি পূরণ করার জন্য লিঙ্কযুক্ত। এই জাতীয় সমস্ত অনুবাদক আউটপুট একটি প্রোগ্রামের ছবিতে সংগ্রহ করা হয় যা এর কার্যকর করার পরিবেশে কার্যকর করার জন্য প্রয়োজনীয় তথ্য ধারণ করে। (জোর আমার)
[পাদটীকা] বাস্তবায়নগুলি অবশ্যই এমন আচরণ করবে যেন এই পৃথক পর্যায়গুলি ঘটে, যদিও অনুশীলনে বিভিন্ন ধাপগুলি একসাথে ভাঁজ করা যেতে পারে।
সংকলনের এই শেষ পর্যায়ে নির্দিষ্ট ত্রুটিগুলি ঘটে থাকে, সাধারণত এটি লিঙ্কিং হিসাবে পরিচিত। এটির মূলত অর্থ এই যে আপনি বাস্তবায়ন ফাইলগুলির একগুচ্ছ অবজেক্ট ফাইল বা লাইব্রেরিতে সংকলন করেছেন এবং এখন আপনি তাদের একসাথে কাজ করতে চান।
বলে আপনি সংজ্ঞাটি সংজ্ঞায়িত a
করেছেন a.cpp
। এখন, প্রতীক b.cpp
ঘোষিত এবং এটি ব্যবহার। লিঙ্ক করার আগে, এটি সহজেই ধরে নেয় যে প্রতীকটি কোথাও সংজ্ঞায়িত হয়েছিল , তবে এটি এখনও কোথায় যত্নশীল নয়। লিঙ্কিং পর্বটি প্রতীকটি সন্ধান করার জন্য এবং এটির সাথে সঠিকভাবে লিঙ্ক করার জন্য দায়ী b.cpp
(ভাল, আসলে এটি ব্যবহার করা অবজেক্ট বা লাইব্রেরিতে)।
আপনি যদি মাইক্রোসফ্ট ভিজ্যুয়াল স্টুডিও ব্যবহার করছেন তবে আপনি দেখতে পাবেন যে প্রকল্পগুলি .lib
ফাইল উত্পন্ন করে। এর মধ্যে রফতানি চিহ্নগুলির একটি টেবিল এবং আমদানিকৃত চিহ্নগুলির একটি টেবিল রয়েছে। আমদানিকৃত প্রতীকগুলি আপনি যে লাইব্রেরির সাথে লিঙ্ক করেন তার বিপরীতে সমাধান করা হয় এবং রফতানি চিহ্নগুলি যে লাইব্রেরিগুলিতে .lib
(যদি থাকে তবে) ব্যবহার করা হয়।
অন্যান্য সংকলক / প্ল্যাটফর্মগুলির জন্য অনুরূপ প্রক্রিয়া বিদ্যমান।
সাধারণ ত্রুটি বার্তা পাঠায় নি error LNK2001
, error LNK1120
, error LNK2019
জন্য Microsoft ভিসুয়াল স্টুডিও এবং 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;
}
জিসিসির সাহায্যে নিম্নলিখিত ত্রুটিগুলি উত্পন্ন করবে :
/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)" (?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
(মাইক্রোসফ্ট ভিজ্যুয়াল স্টুডিও) ব্যবহার করার সময় .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
ক্লাস সংজ্ঞা মধ্যে ইন্টিগ্রাল বা গণনা টাইপের একটি ডেটা সদস্যের জন্য একটি ইনিশিয়ালাইজার সরবরাহ করা যেতে পারে ; তবে, এই সদস্যটির অদ্ভুত-ব্যবহারের জন্য উপরে বর্ণিত হিসাবে এখনও একটি নামের জায়গার স্কোপ সংজ্ঞা প্রয়োজন। সি ++ 11 সমস্ত static const
ডেটা সদস্যের জন্য বর্গের ভিতরে আরম্ভ করার অনুমতি দেয় ।
সাধারণত, প্রতিটি অনুবাদ ইউনিট একটি অবজেক্ট ফাইল তৈরি করবে যা সেই অনুবাদ ইউনিটে সংজ্ঞায়িত চিহ্নগুলির সংজ্ঞা যুক্ত করে। এই চিহ্নগুলি ব্যবহার করতে, আপনাকে সেই অবজেক্ট ফাইলগুলির সাথে লিঙ্ক করতে হবে।
জিসিসি-র অধীনে আপনি কমান্ড লাইনের সাথে যুক্ত হওয়া সমস্ত অবজেক্ট ফাইলগুলি নির্দিষ্ট করতে পারবেন বা বাস্তবায়ন ফাইলগুলি একসাথে সংকলন করবেন।
g++ -o test objectFile1.o objectFile2.o -lLibraryName
libraryName
এখানে শুধু লাইব্রেরির খালি নাম প্ল্যাটফর্ম-নির্দিষ্ট সংযোজন ছাড়া। সুতরাং যেমন লিনাক্স লাইব্রেরিতে ফাইলগুলি সাধারণত বলা হয় libfoo.so
তবে আপনি কেবল লিখতেন -lfoo
। উইন্ডোজে একই ফাইলটি ডাকা হতে পারে foo.lib
তবে আপনি একই যুক্তিটি ব্যবহার করবেন। আপনাকে সেই ডিরেক্টরিটি যুক্ত করতে হবে যেখানে এই ফাইলগুলি ব্যবহার করে পাওয়া যাবে -L‹directory›
। -l
বা পরে কোনও স্থান না লিখে নিশ্চিত করুন -L
।
জন্য উপর XCode : -> লাইব্রেরি অনুসন্ধানের পথ যুক্ত করুন - যোগ করুন ব্যবহারকারী শিরোলেখ অনুসন্ধান পথ> ড্র্যাগ এবং প্রকল্প ফোল্ডারের মধ্যে প্রকৃত গ্রন্থাগার রেফারেন্স ড্রপ।
এমএসভিএসের অধীনে কোনও প্রকল্পে যুক্ত হওয়া ফাইলগুলির সাথে স্বয়ংক্রিয়ভাবে তাদের অবজেক্ট ফাইলগুলি একসাথে যুক্ত থাকে এবং একটি lib
ফাইল উত্পন্ন হয় (সাধারণ ব্যবহারে)। একটি পৃথক প্রকল্পে প্রতীকগুলি ব্যবহার করতে, আপনাকে lib
প্রকল্পের সেটিংসে ফাইলগুলি অন্তর্ভুক্ত করতে হবে । এই প্রকল্পটি বৈশিষ্ট্য Linker অধ্যায় মধ্যে সম্পন্ন করা হয়, এ Input -> Additional Dependencies
। ( lib
ফাইলটিতে পাথ যুক্ত করা উচিত Linker -> General -> Additional Library Directories
) কোনও তৃতীয় পক্ষের লাইব্রেরি যখন কোনও lib
ফাইল সরবরাহ করা হয় তখন ব্যবহার না করার ফলে সাধারণত ত্রুটি হয়।
এটি এমনও হতে পারে যে আপনি সংকলনে ফাইল যুক্ত করতে ভুলে গেছেন, সেই ক্ষেত্রে অবজেক্ট ফাইলটি উত্পন্ন করা হবে না। ইন জিসিসি আপনি কমান্ড লাইন ফাইলগুলি যোগ চাই। ইন MSVS প্রকল্পের ফাইল যোগ করলে তা স্বয়ংক্রিয়ভাবে কম্পাইল (যদিও ফাইল করতে পারেন, ম্যানুয়ালি, স্বতন্ত্রভাবে বিল্ড থেকে বাদ দেওয়া) করতে হবে।
উইন্ডোজ প্রোগ্রামিং-এ, টেল-টেল সাইন যে আপনি কোনও প্রয়োজনীয় লাইব্রেরির সাথে লিঙ্ক করেননি তা হ'ল অমীমাংসিত প্রতীকটির নাম শুরু হয় __imp_
। ডকুমেন্টেশনে ফাংশনের নামটি অনুসন্ধান করুন এবং এটিতে আপনার কোনও লাইব্রেরি ব্যবহার করা উচিত তা বলা উচিত। উদাহরণস্বরূপ, এমএসডিএন প্রতিটি ফাংশনের নীচে একটি বাক্সে "লাইব্রেরি" নামে একটি বিভাগে তথ্য রাখে।
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
আপনি যে ফাংশনটি প্রয়োগ করেছেন সেটি আপনার ঘোষিতটির সাথে ঠিক মেলে যাতে সাবধান হন Be উদাহরণস্বরূপ, আপনার সিভি-কোয়ালিফায়ার মিলতে পারে না:
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
অবশ্যই উপস্থিত হওয়া আবশ্যক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
সুতরাং আবার পুনরাবৃত্তি, যাতে শুরু করে ব্যাপার!
একটি "অপরিজ্ঞাত রেফারেন্স / অমীমাংসিত বাহ্যিক প্রতীক" কী?
আমি "অপরিবর্তিত রেফারেন্স / অমীমাংসিত বাহ্যিক প্রতীক" কী তা বোঝানোর চেষ্টা করব।
দ্রষ্টব্য: আমি জি ++ এবং লিনাক্স ব্যবহার করি এবং সমস্ত উদাহরণ এটির জন্য
উদাহরণস্বরূপ আমাদের কিছু কোড রয়েছে
// 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
এটি ঘটেছে কারণ আমাদের স্থানীয়_ভার_নাম স্থির, অর্থাৎ এটি অন্যান্য মডিউলগুলির জন্য দৃশ্যমান নয়। এখন আরও গভীরভাবে। অনুবাদ পর্ব আউটপুট পান
$ 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
সুতরাং, আমরা দেখেছি স্থানীয়_ভার_নামের জন্য কোনও লেবেল নেই, এজন্য লিঙ্কার এটি খুঁজে পায় নি। তবে আমরা হ্যাকার :) এবং আমরা এটি ঠিক করতে পারি। আপনার পাঠ্য সম্পাদক এবং 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:
; ...
আমরা স্থানীয়_ভর_নামের দৃশ্যমানতা পরিবর্তন করেছি এবং এর মান 456789 তে সেট করেছি it এটি থেকে কোনও অবজেক্ট ফাইল তৈরি করার চেষ্টা করুন
$ g++ -c src1.s -o src2.o
ঠিক আছে, দেখুন পাঠ্য আউটপুট (প্রতীক)
$ readelf --symbols src1.o
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
এখন স্থানীয়_ভর_নামে বাইন্ড গ্লোবাল রয়েছে (লোকাল ছিল)
লিংক
$ g++ src1.o src2.o -o prog
এবং এটি চালান
$ ./prog
123456789
ঠিক আছে, আমরা এটি হ্যাক :)
সুতরাং, ফলস্বরূপ - লিঙ্কার যখন অবজেক্ট ফাইলগুলিতে গ্লোবাল প্রতীকগুলি খুঁজে না পায় তখন একটি "অপরিজ্ঞাত রেফারেন্স / অমীমাংসিত বাহ্যিক প্রতীক ত্রুটি" ঘটে যায়।
ফাংশন (বা ভেরিয়েবল) void foo()
একটি সি প্রোগ্রামে সংজ্ঞায়িত করা হয়েছিল এবং আপনি এটি সি ++ প্রোগ্রামে ব্যবহার করার চেষ্টা করছেন:
void foo();
int main()
{
foo();
}
সি ++ লিঙ্কার নামগুলি ম্যাঙ্গেল হওয়ার প্রত্যাশা করে, তাই আপনাকে ফাংশনটি এই হিসাবে ঘোষণা করতে হবে:
extern "C" void foo();
int main()
{
foo();
}
সমানভাবে, কোনও সি প্রোগ্রামে সংজ্ঞায়িত হওয়ার পরিবর্তে ফাংশনটি (বা ভেরিয়েবল) void foo()
সি ++ এ সংজ্ঞায়িত করা হয়েছিল তবে সি সংযোগের সাথে:
extern "C" void foo();
এবং আপনি এটি সি ++ লিঙ্কেজ সহ কোনও সি ++ প্রোগ্রামে ব্যবহার করার চেষ্টা করছেন।
যদি একটি সম্পূর্ণ লাইব্রেরি একটি শিরোলেখ ফাইলটিতে অন্তর্ভুক্ত থাকে (এবং এটি সি কোড হিসাবে সংকলিত ছিল); অন্তর্ভুক্ত নিম্নলিখিত হিসাবে করা প্রয়োজন হবে;
extern "C" {
#include "cheader.h"
}
#ifdef __cplusplus [\n] extern"C" { [\n] #endif
এবং #ifdef __cplusplus [\n] } [\n] #endif
( [\n]
সত্যিকারের ক্যারেজ রিটার্ন হওয়া সত্ত্বেও আমি মন্তব্যটিতে এটি সঠিকভাবে লিখতে পারি না) রক্ষা করি।
extern "C" { #include <myCppHeader.h> }
।
অন্য সব যদি ব্যর্থ হয় তবে পুনরায় কম্পাইল করুন।
আমি সম্প্রতি আপত্তিজনক ফাইলটি পুনরায় সংশোধন করে ভিজ্যুয়াল স্টুডিও ২০১২ সালে একটি অমীমাংসিত বাহ্যিক ত্রুটি থেকে মুক্তি পেতে সক্ষম হয়েছি। আমি যখন পুনর্নির্মাণ করি তখন ত্রুটিটি চলে যায়।
এটি সাধারণত ঘটে যখন দুটি (বা আরও) লাইব্রেরিতে একটি চক্রীয় নির্ভরতা থাকে। লাইব্রেরি একটি বিলিবে প্রতীক ব্যবহার করার চেষ্টা করে এবং লাইব্রেরি বি এলিব থেকে প্রতীক ব্যবহার করার চেষ্টা করে। কোনটিই দিয়ে শুরু করার অস্তিত্ব নেই। আপনি যখন এটিকে সংকলনের চেষ্টা করবেন, লিঙ্ক পদক্ষেপটি ব্যর্থ হবে কারণ এটি বিলিবকে খুঁজে পাচ্ছে না। 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();
এবং বর্তমান সংযোজনটির সংজ্ঞা রয়েছে বলে সংযোগকারীটিকে ফাংশনটি রফতানি করতে বলে tells যখন কোনও ভিন্ন মডিউলে ঘোষণাটি অন্তর্ভুক্ত করা হয় তখন এটি প্রসারিত হবে
__declspec(dllimport) void foo();
এবং সংকলককে বলে যে সংজ্ঞাটি আপনি যে লাইব্রেরির সাথে সংযুক্ত করেছেন সেগুলির মধ্যে একটিতে রয়েছে (এছাড়াও দেখুন 1 )।
আপনি আমদানি / রফতানি ক্লাসগুলি সমান করতে পারেন:
class DLLIMPEXP X
{
};
visibility
এবং উইন্ডোজ .def
ফাইলগুলি উল্লেখ করা উচিত , কারণ এগুলি প্রতীকের নাম এবং উপস্থিতিকেও প্রভাবিত করে।
.def
যুগে যুগে ফাইল ব্যবহার করি নি n't একটি উত্তর যুক্ত করতে বা এটি সম্পাদনা করতে নির্দ্বিধায়।
এটি প্রতিটি ভিসি ++ প্রোগ্রামাররা আবার সময় এবং সময় দেখেছিল এমন একটি বিভ্রান্তিকর ত্রুটি বার্তা। প্রথমে বিষয়গুলি পরিষ্কার করে নেওয়া যাক।
উ: প্রতীক কী? সংক্ষেপে, একটি প্রতীক একটি নাম। এটি একটি চলক নাম, একটি ফাংশন নাম, শ্রেণীর নাম, একটি টাইপডেফ নাম বা সি ++ ভাষার অন্তর্ভুক্ত names নাম এবং চিহ্নগুলি বাদে কিছু হতে পারে। এটি নির্ভরতা গ্রন্থাগার (অন্য ব্যবহারকারী-সংজ্ঞায়িত) দ্বারা ব্যবহারকারী সংজ্ঞায়িত বা প্রবর্তিত।
খ। বাহ্যিক কী?
ভিসি ++ এ, প্রতিটি উত্স ফাইল (.cpp, .c, ইত্যাদি) অনুবাদ ইউনিট হিসাবে বিবেচিত হয়, সংকলকটি একবারে একটি ইউনিট সংকলন করে এবং বর্তমান অনুবাদ ইউনিটের জন্য একটি অবজেক্ট ফাইল (.obj) উত্পন্ন করে। (দ্রষ্টব্য যে এই উত্স ফাইলটি অন্তর্ভুক্ত প্রতিটি শিরোনাম ফাইল প্রাক প্রসেসড হবে এবং এই অনুবাদ ইউনিটের অংশ হিসাবে বিবেচিত হবে) অনুবাদ ইউনিটের মধ্যে থাকা সমস্ত কিছুকে অভ্যন্তরীণ হিসাবে বিবেচনা করা হয়, অন্য সমস্ত কিছুই বাহ্যিক হিসাবে বিবেচিত হয়। C ++ তোমার মতোই কীওয়ার্ড ব্যবহার করে একটি বহিস্থিত প্রতীক উল্লেখ থাকতে পারে extern
, __declspec (dllimport)
ইত্যাদি।
গ। "সংকল্প" কি? সমাধান একটি সংযোগ-সময় শব্দ term লিঙ্কিং-টাইমে, লিঙ্কার বস্তু ফাইলগুলিতে প্রতিটি চিহ্নের জন্য বাহ্যিক সংজ্ঞাটি অনুসন্ধান করার চেষ্টা করে যা অভ্যন্তরীণভাবে তার সংজ্ঞাটি খুঁজে পায় না। এই অনুসন্ধানের প্রক্রিয়াটির সুযোগগুলি সহ:
এই অনুসন্ধানের প্রক্রিয়াটিকে সংকল্প বলে।
D. অবশেষে, কেন অমীমাংসিত বাহ্যিক প্রতীক? যদি লিঙ্কার কোনও প্রতীকের জন্য বাহ্যিক সংজ্ঞাটি খুঁজে না পায় যার অভ্যন্তরীণ কোনও সংজ্ঞা নেই, তবে এটি একটি অমীমাংসিত বহিরাগত প্রতীক ত্রুটির প্রতিবেদন করে।
E. LNK2019 এর সম্ভাব্য কারণগুলি : অমীমাংসিত বাহ্যিক প্রতীক ত্রুটি। আমরা ইতিমধ্যে জানি যে এই ত্রুটিটি লিঙ্কার বাহ্যিক চিহ্নগুলির সংজ্ঞা পেতে ব্যর্থ হওয়ার কারণে হয়েছে, সম্ভাব্য কারণগুলি এইভাবে সাজানো যেতে পারে:
উদাহরণস্বরূপ, যদি আমাদের a.cpp এ সংজ্ঞায়িত foo নামে একটি ফাংশন থাকে:
int foo()
{
return 0;
}
বি সি পি পি-তে আমরা ফাংশন ফু বলতে চাই, তাই আমরা যুক্ত করি
void foo();
ফাংশন foo () ঘোষণা করতে এবং অন্য ফাংশন বডিতে এটি কল করতে, বলুন bar()
:
void bar()
{
foo();
}
এখন আপনি এই কোডটি তৈরি করার সময় আপনি fo একটি অমীমাংসিত প্রতীক হিসাবে অভিযোগ করে একটি 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);
স্বাক্ষরের পরিবর্তে যেমন উইন্ডোজ অ্যাপ্লিকেশন প্রকল্পগুলিকে এন্ট্রি পয়েন্ট ফাংশনে (উপরের নিখোঁজ রেফারেন্সে উল্লিখিত) বাঁধাই করতে পারে ।
আপনার আইডিই যদি সমতল কনসোল প্রকল্পগুলিকে সমর্থন করে তবে আপনি উইন্ডোজ অ্যাপ্লিকেশন প্রকল্পের পরিবর্তে এই প্রকল্পের ধরণটি চয়ন করতে পারেন।
এখানে বাস্তব কেস সমস্যা থেকে কেস 1 এবং কেস 2 আরও বিশদে পরিচালনা করা হয়েছে ।
নতুন টুলসেট সংস্করণে ভিজ্যুয়াল স্টুডিও নুগেট প্যাকেজ আপডেট করা দরকার
আমার কেবলমাত্র ভিজ্যুয়াল স্টুডিও 2013 এর সাথে লিবিপং লিঙ্ক করার চেষ্টা করার সময় এই সমস্যা হয়েছিল The সমস্যাটি হ'ল প্যাকেজ ফাইলে কেবল ভিজ্যুয়াল স্টুডিও 2010 এবং 2012 এর জন্য লাইব্রেরি ছিল।
সঠিক সমাধানটি আশা করা যায় বিকাশকারী একটি আপডেট হওয়া প্যাকেজ প্রকাশ করে তারপরে আপগ্রেড করে তবে এটি ভিএস2013 এর জন্য অতিরিক্ত সেটিং হ্যাক করে আমার পক্ষে কাজ করেছে, ভিএস ২০১২ লাইব্রেরি ফাইলগুলিতে ইঙ্গিত করে।
আমি সমস্ত বিভাগটি অনুলিপি করে সেই ফাইলটি packages
সন্ধান করে packagename\build\native\packagename.targets
এবং এর ভিতরে প্যাকেজটি ( সমাধানের ডিরেক্টরিতে থাকা ফোল্ডারে) সম্পাদনা করেছি v110
। আমি পরিবর্তন v110
করতে v120
এ অবস্থায় ক্ষেত্র শুধুমাত্র সব ফাইলের নাম পাথ ত্যাগ করার খুব সতর্কতা অবলম্বন হচ্ছে v110
। এটি কেবলমাত্র ভিজ্যুয়াল স্টুডিও 2013 কে 2012 এর লাইব্রেরিতে লিঙ্ক করার অনুমতি দিয়েছে এবং এই ক্ষেত্রে এটি কাজ করেছে।
ধরুন আপনার সি ++ তে একটি বড় প্রকল্প লেখা আছে যার হাজার হাজার .cpp ফাইল এবং হাজার হাজার .h ফাইল রয়েছে nd এবং ধরা যাক প্রকল্পটি দশটি স্ট্যাটিক লাইব্রেরির উপরও নির্ভর করে। আসুন আমরা উইন্ডোতে রয়েছি এবং আমরা ভিজ্যুয়াল স্টুডিও 20XX এ আমাদের প্রকল্পটি তৈরি করি। পুরো সমাধানটি সংকলন শুরু করতে আপনি যখন Ctrl + F7 ভিজ্যুয়াল স্টুডিও টিপেন (ধরুন আমাদের সমাধানে কেবল একটি প্রকল্প আছে)
সংকলনের অর্থ কী?
সংকলনের দ্বিতীয় ধাপটি লিঙ্কার দ্বারা সম্পন্ন হয় L লিঙ্কারকে সমস্ত অবজেক্ট ফাইলটি মার্জ করে শেষ পর্যন্ত আউটপুট তৈরি করতে হবে (যা এক্সিকিউটেবল বা একটি লাইব্রেরি হতে পারে)
একটি প্রকল্পের লিঙ্কে পদক্ষেপ
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
পর্যবেক্ষণ
এই ধরণের ত্রুটি কীভাবে সমাধান করবেন
সংকলক সময় ত্রুটি:
লিঙ্কারের সময় ত্রুটি
#pragma once
সংকলককে একটি শিরোনাম অন্তর্ভুক্ত না করার জন্য ব্যবহার করুন যদি এটি ইতিমধ্যে বর্তমান .cpp অন্তর্ভুক্ত থাকে যা সংকলিত হয়আমার সম্প্রতি এই সমস্যা হয়েছিল এবং এটি ভিজ্যুয়াল স্টুডিও এক্সপ্রেস 2013-এ একটি বাগ ছিল was । আমাকে প্রকল্প থেকে একটি উত্স ফাইল সরিয়ে ফেলা হয়েছে এবং বাগটি কাটিয়ে উঠতে আবার যুক্ত করতে হয়েছিল।
যদি আপনি বিশ্বাস করেন যে এটি সংকলক / আইডিইতে বাগ হতে পারে তবে চেষ্টা করার পদক্ষেপগুলি:
বেশিরভাগ আধুনিক লিকারগুলিতে একটি ভার্বোস বিকল্প অন্তর্ভুক্ত থাকে যা বিভিন্ন ডিগ্রীতে প্রিন্ট করে;
জিসিসি এবং ঝনঝন জন্য; আপনি সাধারণত যুক্ত -v -Wl,--verbose
বা -v -Wl,-v
কমান্ড লাইনে যোগ করতে হবে । আরো বিস্তারিত এখানে পাওয়া যাবে;
এমএসভিসির জন্য, /VERBOSE
(বিশেষত /VERBOSE:LIB
) লিঙ্ক কমান্ড লাইনে যুক্ত করা হয়েছে।
/VERBOSE
লিঙ্কার অপশনে এমএসডিএন পৃষ্ঠা ।লিঙ্কযুক্ত .lib ফাইল একটি .dll এর সাথে সম্পর্কিত
আমারও একই প্রশ্ন ছিল. বলুন আমার কাছে মাইপ্রজেক্ট এবং টেস্টপ্রজেক্ট রয়েছে। আমি মাইপ্রজেক্টের জন্য lib ফাইলটিকে কার্যকরভাবে টেস্টপ্রজেক্টের সাথে সংযুক্ত করেছি। যাইহোক, এই লিব ফাইলটি মাইপ্রজেক্টটি নির্মিত হওয়ার জন্য ডিএলএল হিসাবে তৈরি করা হয়েছিল। এছাড়াও, আমি মাইপ্রজেক্টে সমস্ত পদ্ধতির সোর্স কোডটি ধারণ করে নি, তবে কেবলমাত্র ডিএলএল-এর প্রবেশ পয়েন্টগুলিতে অ্যাক্সেস করেছি।
সমস্যাটি সমাধান করার জন্য, আমি মাইপ্রজেক্টকে এলআইবি হিসাবে তৈরি করেছি এবং টেস্টপ্রজেক্টটিকে এই .lib ফাইলের সাথে সংযুক্ত করেছি (আমি উত্পন্ন .lib ফাইলটিকে টেস্টপ্রজেক্ট ফোল্ডারে আটকানো কপি করি)। আমি তখন আবার ডিএলএল হিসাবে মাইপ্রজেক্টটি তৈরি করতে পারি can টেস্টপ্রজেক্টটি যে লিবে লিঙ্কযুক্ত তা মাইপ্রজেক্টের ক্লাসে সমস্ত পদ্ধতির কোড ধারণ করে এটি সংকলন করে চলেছে।
যেহেতু লিঙ্কার ত্রুটিগুলি আসে তখন লোকেরা এই প্রশ্নের দিকে পরিচালিত বলে মনে হয় আমি এটি এখানে যুক্ত করতে যাচ্ছি।
জিসিসি 5.2.0 এর সাথে লিঙ্কার ত্রুটির একটি সম্ভাব্য কারণ হ'ল একটি নতুন libstdc ++ লাইব্রেরি এখন ডিফল্টরূপে বেছে নেওয়া হয়েছে।
যদি আপনি স্ট্যান্ডার্ড :: __ সিএক্সএক্সএল 11 নামপঞ্জী বা ট্যাগ [আবি: সিএক্সএক্স 11]-তে টাইপের সাথে সংযুক্ত চিহ্নগুলির অপরিবর্তিত রেফারেন্স সম্পর্কে লিঙ্কার ত্রুটি পেয়ে থাকেন তবে সম্ভবত এটি ইঙ্গিত দেয় যে আপনি _GLIBCXX_USE_CXX11_ABI এর জন্য বিভিন্ন মানের সাথে সংকলিত অবজেক্ট ফাইলগুলি একসাথে যুক্ত করার চেষ্টা করছেন indicates ম্যাক্রো। এটি সাধারণত তৃতীয় পক্ষের লাইব্রেরির সাথে সংযোগ করার সময় ঘটে যা জিসিসির একটি পুরানো সংস্করণ দিয়ে সংকলিত হয়েছিল। যদি তৃতীয় পক্ষের লাইব্রেরিটি নতুন এবিআই দিয়ে পুনর্নির্মাণ করা না যায় তবে আপনাকে পুরানো এবিআইয়ের সাথে আপনার কোডটি পুনরায় সংকলন করতে হবে।
সুতরাং আপনি যদি 5.1.0 এর পরে কোনও জিসিসিতে স্যুইচ করার সময় হঠাৎ লিঙ্কার ত্রুটিগুলি পান তবে এটি চেক আউট করার বিষয় হবে।
GNU ld এর চারপাশে একটি মোড়ক যা লিঙ্কার স্ক্রিপ্টগুলি সমর্থন করে না
কিছু .so ফাইলগুলি আসলে জিএনইউ এলডি লিঙ্কার স্ক্রিপ্ট হয় , যেমন libtbb.so ফাইল এই বিষয়বস্তু সহ একটি ASCII পাঠ্য ফাইল:
INPUT (libtbb.so.2)
আরও কিছু জটিল বিল্ড এটি সমর্থন নাও করতে পারে। উদাহরণস্বরূপ, আপনি যদি সংকলক বিকল্পগুলিতে -v অন্তর্ভুক্ত করেন তবে আপনি দেখতে পারেন যে মেইনউইন জিসিসি র্যাপার এমডুভিপ লিঙ্কারে লিখিতভাবে ভার্জ বোতাম আউটপুট তালিকার মধ্যে লিঙ্কার স্ক্রিপ্ট কমান্ড ফাইলগুলি বাতিল করে দেয় around চারপাশে একটি সহজ কাজ হল লিঙ্কার স্ক্রিপ্ট ইনপুট কমান্ড প্রতিস্থাপন করা around পরিবর্তে ফাইলের একটি অনুলিপি (বা একটি সিমলিংক) সহ ফাইল করুন, যেমন
cp libtbb.so.2 libtbb.so
অথবা আপনি .l এর পুরো পথ দিয়ে -l যুক্তিটি প্রতিস্থাপন করতে পারেন, যেমন -ltbb
না করে/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
libfoo
নির্ভর করে libbar
তবে আপনার লিঙ্কেজটি সঠিকভাবে libfoo
আগে রাখে libbar
।undefined reference to
কিছু ত্রুটির সাথে ব্যর্থ হয় ।#include
এবং আপনি যে লাইব্রেরিগুলিতে সংযুক্ত হন তা প্রকৃতপক্ষে সংজ্ঞায়িত হয়।উদাহরণ সি তে রয়েছে তারা সমানভাবে সি ++ হতে পারে be
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
পড়া এখানে থেকে isচ্ছিক ।
ডিফল্টরূপে, জিসিসি দ্বারা উত্পাদিত একটি লিংকেজ কমান্ড, আপনার ডিস্ট্রোতে, লিঙ্কেজে থাকা ফাইলগুলি বাম থেকে ডানদিকে কমান্ডলাইন অনুক্রমে গ্রাস করে। যখন এটি দেখতে পেল যে কোনও ফাইল কোনও কিছুকে বোঝায় কোনও এবং এর সংজ্ঞা নেই, তখন ডানে আরও ফাইলগুলিতে একটি সংজ্ঞা অনুসন্ধান করবেন। যদি শেষ পর্যন্ত এটি একটি সংজ্ঞা খুঁজে পায়, তবে রেফারেন্সটি সমাধান হয়ে যায়। যদি কোনও রেফারেন্স শেষে অমীমাংসিত থাকে, লিঙ্কেজ ব্যর্থ হয়: লিঙ্কার পিছন দিকে সন্ধান করে না।
স্থির লাইব্রেরি সহ প্রথম, উদাহরণ 1my_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
linker যোগ করেনি eg1.o
প্রোগ্রাম যখন এটি সূচিত
-lmy_lib
। কারণ যে সময়ে, এটি দেখা যায় নি eg1.o
। তোমার প্রোগ্রাম এখনো কোনো রেফারেন্স মনে করার কারণ নাই hw
: এটা এখনো কোনো রেফারেন্স দেখা যায় না সব সময়ে , কারণ সমস্ত রেফারেন্স এটা তোলে হয় eg1.o
।
সুতরাং লিঙ্কারটি my_lib.o
প্রোগ্রামটিতে যুক্ত হয় না এবং এর জন্য আর কোনও ব্যবহার নেই libmy_lib.a
।
এরপরে এটি সন্ধান করে eg1.o
এবং এটি প্রোগ্রাম হিসাবে যুক্ত করে। লিঙ্কেজ সিকোয়েন্সের একটি অবজেক্ট ফাইল সর্বদা প্রোগ্রামে যুক্ত করা হয়। এখন, প্রোগ্রামটি একটি রেফারেন্স দেয় hw
এবং এর কোনও সংজ্ঞা থাকে না hw
; তবে লিঙ্কেজ সিকোয়েন্সে এমন কিছুই অবশিষ্ট নেই যা অনুপস্থিত সংজ্ঞাটি সরবরাহ করতে পারে। অমীমাংসিত হয়ে hw
শেষ হয়ে যায় এবং সংযোগ ব্যর্থ হয়।
দ্বিতীয়, ভাগ করা লাইব্রেরি সহ উদাহরণ 2libz
একটি ভাগ করা লাইব্রেরি অবজেক্ট ফাইল বা এর মতো কোনও কিছুর সংরক্ষণাগার নয়। এটি অনেকটা এমন একটি প্রোগ্রামের মতো যার কোনও main
ফাংশন নেই এবং পরিবর্তে এটি সংজ্ঞায়িত করা একাধিক অন্যান্য প্রতীক প্রকাশ করে, যাতে অন্যান্য প্রোগ্রামগুলি রানটাইমে এগুলি ব্যবহার করতে পারে।
অনেক লিনাক্স ডিস্ট্রো আজকে তাদের জিসিসি টুলচেইন যাতে তার ভাষা ড্রাইভার (কনফিগার gcc
, g++
, gfortran
ইত্যাদি) সিস্টেম linker (নির্দেশ 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. মুহুর্তে মত ঠিক একই ভাবে ভুল যখন linker খুঁজে বের করে -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
অবশ্যই gcc কে eg1.c
প্রথমে সংকলন করতে হবে , এবং তারপরে ফলাফলযুক্ত অবজেক্ট ফাইলটি লিঙ্ক করতে হবে libmy_lib.a
। সুতরাং এটি কীভাবে জানতে পারে না যে লিংকটি করার সময় অবজেক্ট ফাইলটি প্রয়োজন?
কারণ একটি একক কমান্ডের সাথে সংকলন এবং সংযোগ লিঙ্কেজ ক্রমের ক্রম পরিবর্তন করে না।
আপনি যখন উপরের কমান্ডটি চালাবেন, তখন এটি নির্ধারণ করুন gcc
যে আপনি সংকলন + লিঙ্কেজ চান। পর্দার অন্তরালে এটি একটি সংকলন কমান্ড তৈরি করে এবং এটি চালায়, তারপরে একটি লিঙ্কেজ কমান্ড তৈরি করে এবং এটি চালায়, যেন আপনি দুটি কমান্ড চালিয়েছেন:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
তাই দুটো ঘটনার ব্যর্থ আপনি ঠিক যেমন এটা আছে না ঐ দুই কমান্ড চালানো। ব্যর্থতায় আপনি যে পার্থক্যটি লক্ষ্য করছেন তা হ'ল gcc কম্পাইল + লিঙ্কের ক্ষেত্রে একটি অস্থায়ী অবজেক্ট ফাইল তৈরি করেছে, কারণ আপনি এটি ব্যবহার করতে বলছেন না 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)
{ /*...*/ }
// ...
};
দ্রষ্টব্য , যখন অপারেটরের ঘোষণাপত্র (বা ফাংশন) কেবল ক্লাসে উপস্থিত হয়, নামটি "সাধারণ" অনুসন্ধানের জন্য পাওয়া যায় না, কেবল যুক্তি নির্ভর নির্ভর অনুসন্ধানের জন্য, সিপ্রেফারেন্স থেকে ;
শ্রেণি বা শ্রেণীর টেম্পলেটর মধ্যে বন্ধুত্বের ঘোষণার মধ্যে প্রথমে ঘোষিত একটি নাম এক্স এর অন্তর্নিহিত সংলগ্ন নেমস্পেসের সদস্য হয়ে যায় তবে নাম অবস্থানের স্কোপের সাথে ম্যাচিং ডিক্লেয়ারেশন না থাকলে অনুসন্ধানের জন্য (যুক্তি-নির্ভর লুকে বাদ দেওয়া যা এক্সকে বিবেচনা করে) অ্যাক্সেসযোগ্য নয় unless প্ ...
Cppreferences এবং 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
(আমি উদাহরণটিতে এখানে একটি পৃথক পথ অন্তর্ভুক্ত করে স্পষ্ট করে দিয়েছি তবে এটি সর্বদা এতটা সুস্পষ্ট নাও হতে পারে Maybe সংকলক সেটিংসে অন্তর্ভুক্ত পথটি পৃথক হতে পারে) ।
এই উদাহরণে নোট করুন, লিঙ্কারটি আপনাকে বলবে এটি এটি খুঁজে পায়নি draw()
, যখন বাস্তবে আপনি জানেন যে এটি অবশ্যই লাইব্রেরি দ্বারা রফতানি করা হচ্ছে। কী ভুল হয়েছে তা ভেবে আপনি আপনার মাথা আঁচড়ান কয়েক ঘন্টা ব্যয় করতে পারেন। জিনিসটি হল, লিঙ্কারটি একটি আলাদা স্বাক্ষর দেখায় কারণ প্যারামিটারের ধরনগুলি কিছুটা আলাদা। উদাহরণস্বরূপ, vec3
উভয় প্রকল্পের মধ্যে যেমন কম্পাইলারের সাথে সম্পর্কিত হয় তবে তা আলাদা। এটি ঘটতে পারে কারণ তারা দুটি সামান্য আলাদা ফাইলগুলি অন্তর্ভুক্ত করে আসে (সম্ভবত ফাইলগুলি লাইব্রেরির দুটি পৃথক সংস্করণ থেকে আসে)।
ডুম্পবিন আপনার বন্ধু, যদি আপনি ভিজুয়াল স্টুডিও ব্যবহার করেন। আমি নিশ্চিত যে অন্যান্য সংকলকগুলির কাছে অন্যান্য অনুরূপ সরঞ্জাম রয়েছে।
প্রক্রিয়াটি এরকম হয়:
[1] প্রকল্পের দ্বারা আমার অর্থ উত্স ফাইলগুলির একটি সেট যা একটি লাইব্রেরি বা এক্সিকিউটেবল উত্পাদন করতে একসাথে যুক্ত linked
সম্পাদনা 1: বুঝতে সহজ হতে প্রথম বিভাগটি পুনরায় লিখেছেন। অন্য কিছু ঠিক করার দরকার আছে কিনা তা জানতে দয়া করে নীচে মন্তব্য করুন। ধন্যবাদ!
UNICODE
সংজ্ঞাএকটি উইন্ডোজ ইউনিকোড বিল্ড সঙ্গে নির্মিত হয় TCHAR
ইত্যাদি হিসাবে সংজ্ঞায়িত করা হচ্ছে wchar_t
সঙ্গে বিল্ডিং না কখন ইত্যাদি UNICODE
সঙ্গে বিল্ড হিসাবে সংজ্ঞায়িত TCHAR
হিসাবে সংজ্ঞায়িত করা char
ইত্যাদি.এই UNICODE
এবং _UNICODE
সংজ্ঞায়িত সব প্রভাবিত " T
" STRING ধরনের ; LPTSTR
, LPCTSTR
এবং তাদের এলক
UNICODE
সংজ্ঞায়িত করে একটি লাইব্রেরি তৈরি করা এবং এটি এমন কোনও প্রকল্পে সংযুক্ত করার চেষ্টা করা যেখানে UNICODE
সংজ্ঞায়িত করা হয়নি, যার ফলে সংযোগকারী ত্রুটি ঘটবে কারণ সংজ্ঞাতে কোনও মিল নেই TCHAR
; char
বনাম wchar_t
।
ত্রুটিটি সাধারণত একটি ফাংশনকে একটি মান char
বা wchar_t
উত্পন্ন প্রকারের সাথে অন্তর্ভুক্ত করে, এতে std::basic_string<>
এগুলিও অন্তর্ভুক্ত থাকতে পারে । কোডে প্রভাবিত ক্রিয়াকলাপটি ব্রাউজ করার সময়, প্রায়শই এখানে TCHAR
বা এর উল্লেখ পাওয়া যায় std::basic_string<TCHAR>
This এটি একটি কথিত চিহ্ন যা কোডটি মূলত একটি ইউনিকোডে এবং মাল্টি-বাইট অক্ষর (বা "সংকীর্ণ") উভয়ের জন্যই তৈরি করা হয়েছিল sign ।
এটি সংশোধন করার জন্য, UNICODE
(এবং _UNICODE
) এর ধারাবাহিক সংজ্ঞা সহ প্রয়োজনীয় সমস্ত গ্রন্থাগার এবং প্রকল্পগুলি তৈরি করুন ।
এটি হয় সঙ্গে করা যেতে পারে;
#define UNICODE
#define _UNICODE
বা প্রকল্পের সেটিংসে;
প্রকল্পের বৈশিষ্ট্য> সাধারণ> প্রকল্পের ডিফল্ট> অক্ষর সেট
বা কমান্ড লাইনে;
/DUNICODE /D_UNICODE
বিকল্পটি প্রযোজ্য, যদি ইউনিকোড ব্যবহারের উদ্দেশ্যে না হয় তবে নিশ্চিত করুন যে সংজ্ঞাগুলি সেট করা নেই এবং / অথবা মাল্টি-ক্যারেক্টর সেটিং প্রকল্পগুলিতে ব্যবহৃত হয়েছে এবং ধারাবাহিকভাবে প্রয়োগ করা হয়েছে।
"রিলিজ" এবং "ডিবাগ" পাশাপাশি তৈরির মধ্যেও সামঞ্জস্য রাখতে ভুলবেন না।
বিল্ডটির একটি "পরিষ্কার" পূর্ববর্তী বিল্ডগুলি, ব্যর্থ বিল্ডস, অসম্পূর্ণ বিল্ডস এবং অন্যান্য বিল্ড সিস্টেম সম্পর্কিত বিল্ড সংক্রান্ত সমস্যাগুলি থেকে পড়ে থাকা "মৃত কাঠ" সরিয়ে ফেলতে পারে।
সাধারণভাবে আইডিই বা বিল্ডের মধ্যে কিছু ধরণের "ক্লিন" ফাংশন অন্তর্ভুক্ত থাকে তবে এটি সঠিকভাবে কনফিগার করা যায় না (যেমন ম্যানুয়াল মেকফাইলে) বা ব্যর্থ হতে পারে (যেমন মধ্যবর্তী বা ফলস্বরূপ দ্বিপদী কেবল পঠনযোগ্য)।
একবার "ক্লিন" শেষ হয়ে গেলে, পরীক্ষা করুন যে "ক্লিন" সফল হয়েছে এবং সমস্ত উত্পন্ন ইন্টারমিডিয়েট ফাইল (যেমন একটি স্বয়ংক্রিয় মেকফিল) সফলভাবে মুছে ফেলা হয়েছে।
এই প্রক্রিয়াটি একটি চূড়ান্ত অবলম্বন হিসাবে দেখা যেতে পারে, তবে প্রায়শই এটি প্রথম প্রথম পদক্ষেপ ; বিশেষত যদি ত্রুটির সাথে সম্পর্কিত কোডটি সম্প্রতি যুক্ত করা হয়েছে (হয় স্থানীয়ভাবে বা উত্স সংগ্রহস্থল থেকে)।
const
পরিবর্তনীয় ঘোষণা / সংজ্ঞাগুলিতে "বাহ্যিক" অনুপস্থিত (কেবলমাত্র 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
: ফাইল সিস্টেমটি সি ++ 17 সাল থেকে স্ট্যান্ডার্ড লাইব্রেরিতে রয়েছে তবে আমার প্রোগ্রামটি সি ++ 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:
Main.cpp এবং file.h. এ ব্যবহৃত বিভিন্ন লাইব্রেরি নোট করুন। যেহেতু main.cpp # < ফাইল সিস্টেম > এর পরে " file.h " অন্তর্ভুক্ত করেছে , তাই সেখানে ব্যবহৃত ফাইল সিস্টেমের সংস্করণটি C ++ 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
।
এটি ঠিক করার জন্য আমার কেবলমাত্র ফাইল পরীক্ষায় <পরীক্ষামূলক :: ফাইল সিস্টেম> পরিবর্তন করতে হবে <ফাইলসিসটাম> এ ।
জিসিসির ডিফল্ট আচরণ হ'ল সমস্ত প্রতীক দৃশ্যমান। যাইহোক, অনুবাদ ইউনিটগুলি বিকল্পের সাহায্যে নির্মিত হলে ফলাফলগুলি ভাগ করা অবজেক্টের -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
(যদি সি ++ ব্যবহার করা হত)।
উইন্ডোজ-ডেলসের অনুরূপ, কোনও একটি সংজ্ঞায়িত জনসাধারণের কার্যাদি চিহ্নিত করবে, উদাহরণস্বরূপ DLL_PUBLIC
:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
যা মোটামুটি উইন্ডোজ / এমএসভিসি-সংস্করণের সাথে মিলে যায়:
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
দৃশ্যমানতা সম্পর্কে আরও তথ্য জিসিসি উইকিতে পাওয়া যাবে।
যখন কোনও অনুবাদ ইউনিট -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
উদাহরণস্বরূপ )।
ম্যাকোজে, একই ফাইলে উভয় আর্কিটেকচার শিপিংয়ের বিকল্প রয়েছে। এটি লিঙ্কটি উভয় সংস্করণ থাকবে বলে আশাবাদী, তবে কেবল একটি। এটি লাইব্রেরিটি বাছাই করা হয়েছে এমন ভুল lib
/ lib64
ফোল্ডারের ক্ষেত্রেও সমস্যা হতে পারে ।