আমাদের কেন সি ++ এ বাহ্যিক "সি" - # অন্তর্ভুক্ত <foo.h>> দরকার?


136

আমাদের কেন ব্যবহার করতে হবে:

extern "C" {
#include <foo.h>
}

বিশেষ করে:

  • আমাদের কখন এটি ব্যবহার করা উচিত?

  • সংকলক / লিঙ্কার স্তরে কী হচ্ছে যা আমাদের এটি ব্যবহার করা প্রয়োজন?

  • সংকলন / সংযোগের ক্ষেত্রে কীভাবে এটি আমাদের যে সমস্যার প্রয়োজন তা সমাধান করে?

উত্তর:


122

সি এবং সি ++ অতিপরিচিতরূপে সমান, তবে প্রতিটি কোডের একটি খুব আলাদা সেটগুলিতে সংকলন করে। আপনি যখন একটি সি ++ সংকলক সহ একটি শিরোনাম ফাইল অন্তর্ভুক্ত করেন, সংকলকটি সি ++ কোড আশা করে। তবে, যদি এটি সি সি শিরোনাম হয়, তবে সংকলকটি শিরোনাম ফাইলটিতে থাকা ডেটা নির্দিষ্ট ফর্ম্যাট C সি ++ 'এবিআই' বা 'অ্যাপ্লিকেশন বাইনারি ইন্টারফেস' তে সংকলিত হওয়ার প্রত্যাশা করে, তাই লিঙ্কারটি চিপ আপ হয়ে যায়। সি তথ্য প্রত্যাশা করে কোনও ফাংশনে সি ++ ডেটা উত্তরণ করা ভাল।

(সত্যিকার অর্থেই কৌতূহল পেতে, সি ++ এর এবিআই সাধারণত তাদের ফাংশন / পদ্ধতির নামগুলি 'মঙ্গল' করে, তাই printf()কোনও সি ফাংশন হিসাবে প্রোটোটাইপকে পতাকা না দিয়ে কল করা , সি ++ আসলে কোড কলিং _Zprintf, এবং শেষে অতিরিক্ত ক্র্যাপ তৈরি করে। )

সুতরাং: extern "C" {...}এসি হেডার সহ যখন ব্যবহার করুন — এটি এত সহজ that অন্যথায়, আপনার সংকলিত কোডটিতে একটি অমিল আছে, এবং লিঙ্কার শ্বাসরোধ করবে। বেশিরভাগ শিরোনামের জন্য, আপনার এমনকি এমনকি প্রয়োজনও হবে না externকারণ বেশিরভাগ সিস্টেম সি শিরোনাম ইতিমধ্যে externতাদের সি ++ কোড এবং ইতিমধ্যে তাদের কোড দ্বারা অন্তর্ভুক্ত করা যেতে পারে যে অ্যাকাউন্টটির জন্য অ্যাকাউন্ট করবে ।


1
আপনি কী দয়া করে আরও সবিস্তারে ব্যাখ্যা করতে পারেন "বেশিরভাগ সিস্টেম সি শিরোনামগুলি ইতিমধ্যে এই বিষয়টির জন্য অ্যাকাউন্ট করবে যে তারা সি ++ কোড দ্বারা অন্তর্ভুক্ত থাকতে পারে এবং ইতিমধ্যে তাদের কোডটি বহিরাগত রয়েছে।" ?
বুলাত এম।

7
@BulatM। এগুলিতে এরকম কিছু থাকে: #ifdef __cplusplus extern "C" { #endif সুতরাং যখন সি ++ ফাইল থেকে অন্তর্ভুক্ত করা হয় তখনও তারা সি হেডার হিসাবে বিবেচিত হয়।
ক্যালমারিয়াস

111

বাহ্যিক "সি" নির্ধারণ করে যে উত্পন্ন বস্তু ফাইলের চিহ্নগুলি কীভাবে নামকরণ করা উচিত। যদি কোনও ফাংশন বহিরাগত "সি" ছাড়াই ঘোষণা করা হয় তবে অবজেক্ট ফাইলে প্রতীক নামটি সি ++ নাম মংলিং ব্যবহার করবে। এখানে একটি উদাহরণ।

দেওয়া হয়েছে পরীক্ষা.সি এর মত:

void foo() { }

অবজেক্ট ফাইলে সংকলন এবং তালিকা প্রতীকগুলি দেয়:

$ g++ -c test.C
$ nm test.o
0000000000000000 T _Z3foov
                 U __gxx_personality_v0

Foo ফাংশনটি আসলে "_Z3foov" নামে পরিচিত। এই স্ট্রিংটিতে রিটার্নের ধরণ এবং অন্যান্য বিষয়গুলির সাথে পরামিতিগুলির জন্য টাইপ তথ্য রয়েছে। আপনি যদি পরিবর্তে পরীক্ষা লিখেন Cসি এটির মতো:

extern "C" {
    void foo() { }
}

তারপরে সংকলন করুন এবং প্রতীকগুলি দেখুন:

$ g++ -c test.C
$ nm test.o
                 U __gxx_personality_v0
0000000000000000 T foo

আপনি সি সংযোগ পেতে। অবজেক্ট ফাইলে "foo" ফাংশনের নামটি কেবল "foo", এবং এর মধ্যে ম্যাঙ্গালিং নামটি থেকে আসা সমস্ত অভিনব ধরণের তথ্য নেই।

আপনি বহিরাগত "সি" এর মধ্যে সাধারণত একটি শিরোনাম অন্তর্ভুক্ত করেন {} যদি এর সাথে যে কোডটি যায় সেগুলি একটি সি সংকলক দিয়ে সংকলিত হয় তবে আপনি এটি সি ++ থেকে কল করার চেষ্টা করছেন। আপনি যখন এটি করেন, আপনি সংকলককে বলছেন যে শিরোনামের সমস্ত ঘোষণাগুলি সি লিঙ্কেজ ব্যবহার করবে। আপনি যখন আপনার কোডটি লিঙ্ক করবেন, আপনার .o ফাইলগুলিতে "_ foo" তে উল্লেখ থাকবে, "_Z3fooblah" নয়, যা আশা করা যায় যে আপনি যে লাইব্রেরির সাথে সংযোগ করছেন তার সাথে মিলবে।

বেশিরভাগ আধুনিক গ্রন্থাগারগুলি এই জাতীয় শিরোনামের চারপাশে প্রহরী রাখবে যাতে প্রতীকগুলি সঠিক লিঙ্কেজের সাথে ঘোষণা করা হয়। উদাহরণস্বরূপ প্রচুর স্ট্যান্ডার্ড শিরোনাম আপনি পাবেন:

#ifdef __cplusplus
extern "C" {
#endif

... declarations ...

#ifdef __cplusplus
}
#endif

এটি নিশ্চিত করে যে যখন সি ++ কোড শিরোনামটি অন্তর্ভুক্ত করে, তখন আপনার অবজেক্ট ফাইলে চিহ্নগুলি সি লাইব্রেরিতে থাকা কি মিলছে। আপনার সি হেডারটির চারপাশে কেবলমাত্র বহিরাগত "সি" put put লাগানো উচিত যদি এটির বয়স্ক থাকে এবং ইতিমধ্যে এই রক্ষক না থাকে।


22

সি ++ এ আপনার বিভিন্ন নাম থাকতে পারে যা একটি নাম ভাগ করে নিতে পারে। উদাহরণস্বরূপ এখানে foo নামক সমস্ত ফাংশনগুলির তালিকা রয়েছে :

  • A::foo()
  • B::foo()
  • C::foo(int)
  • C::foo(std::string)

তাদের সবার মধ্যে পার্থক্য তৈরি করতে, সি ++ সংকলক নাম-ম্যাংলিং বা সাজসজ্জা নামে একটি প্রক্রিয়াতে প্রত্যেকটির জন্য অনন্য নাম তৈরি করবে। সি সংকলকরা এটি করবেন না। তদ্ব্যতীত, প্রতিটি সি ++ সংকলক এটি করতে পারে এটি একটি ভিন্ন উপায়।

বাহ্যিক "সি" ব্রেসগুলির মধ্যে কোডে কোনও নাম-ম্যাংলিং না করতে সি ++ সংকলককে বলে। এটি আপনাকে সি ++ এর মধ্যে থেকে সি ফাংশনগুলিতে কল করতে দেয়।


14

বিভিন্ন সংকলক যেভাবে নাম-ম্যাংলিং সম্পাদন করে সেটির সাথে এটি করতে হবে। একটি সি ++ সংকলক শিরোনাম ফাইলটি থেকে সি-সংকলকটির চেয়ে সম্পূর্ণ ভিন্ন উপায়ে রফতানি করা চিহ্নটির নাম ছড়িয়ে দেবে, সুতরাং আপনি যখন লিঙ্ক দেওয়ার চেষ্টা করবেন, তখন আপনাকে একটি লিঙ্কারের ত্রুটি পাওয়া যাবে যে এখানে চিহ্নগুলি অনুপস্থিত ছিল।

এটি সমাধানের জন্য, আমরা সি ++ সংকলককে "সি" মোডে চলতে বলি, সুতরাং এটি সি সংকলকটির মতো নাম ম্যাংলিং সম্পাদন করে। এটি করার পরে, লিঙ্কারের ত্রুটিগুলি স্থির হয়ে গেছে।


11

সি এবং সি ++ এর চিহ্নগুলির নাম সম্পর্কে বিভিন্ন বিধি রয়েছে। চিহ্নগুলি কীভাবে লিঙ্কার জানে যে কম্পাইলার দ্বারা উত্পাদিত একটি অবজেক্ট ফাইলে "ওপেনব্যাঙ্কএকউন্ট" ফাংশন করার কলটি সেই ফাংশনের একটি রেফারেন্স যা আপনি একই (বা সামঞ্জস্যপূর্ণ) দ্বারা ভিন্ন উত্স ফাইল থেকে উত্পাদিত অন্য অবজেক্ট ফাইলে "ওপেনব্যাঙ্ক অ্যাকাউন্ট" নামে পরিচিত called কম্পাইলার। এটি আপনাকে একাধিক উত্স ফাইল থেকে একটি প্রোগ্রাম তৈরি করতে দেয় যা কোনও বড় প্রকল্পে কাজ করার সময় স্বস্তি হয়।

সিটিতে নিয়মটি খুব সহজ, প্রতীকগুলি যে কোনওভাবেই একক নামের জায়গাতে থাকে। সুতরাং পূর্ণসংখ্যা "মোজা" "মোজা" হিসাবে সংরক্ষণ করা হয় এবং ফাংশন কাউন্ট_সোকসগুলি "কাউন্টি_সোকস" হিসাবে সংরক্ষণ করা হয়।

এই সাধারণ প্রতীক নামকরণের নিয়ম সহ সি এবং অন্যান্য ভাষার জন্য লিঙ্কারগুলি তৈরি করা হয়েছিল। সুতরাং লিঙ্কারে থাকা চিহ্নগুলি কেবল সহজ স্ট্রিং।

তবে সি ++ এ ভাষা আপনাকে নেমস্পেস এবং পলিমারফিজম এবং এই জাতীয় সরল নিয়মের সাথে দ্বিধাবিভক্ত বিভিন্ন জিনিস দেয়। আপনার "ছয়" নামক পলিমারফিক ফাংশনের সমস্ত ছয়টির আলাদা আলাদা চিহ্ন থাকা দরকার, বা অন্যটি অবজেক্ট ফাইল দ্বারা ভুলটি ব্যবহার করা হবে। এটি "ম্যাংলিং" (এটি একটি প্রযুক্তিগত শব্দ) প্রতীকগুলির নাম দ্বারা সম্পন্ন হয়।

সি লাইব্রেরি বা কোডের সাথে সি ++ কোডের লিঙ্ক করার সময়, আপনাকে সি এর মধ্যে "বাইরের কিছু" সি লেখা আছে যেমন সি লাইব্রেরির জন্য শিরোনাম ফাইলগুলি আপনার সি ++ সংকলককে বলতে হবে যে এই চিহ্নগুলির নাম ম্যাঙ্গেল করা উচিত নয়, যখন বাকি অংশগুলি আপনার সি ++ কোড অবশ্যই ম্যাংড করা উচিত বা এটি কাজ করবে না।


11

আমাদের কখন এটি ব্যবহার করা উচিত?

আপনি যখন সি লাইবেরিগুলিকে সি ++ অবজেক্ট ফাইলে সংযুক্ত করছেন

সংকলক / লিঙ্কার স্তরে কী হচ্ছে যা আমাদের এটি ব্যবহার করা প্রয়োজন?

সি এবং সি ++ প্রতীক নামকরণের জন্য বিভিন্ন স্কিম ব্যবহার করে। প্রদত্ত লাইব্রেরিতে লিঙ্ক করার সময় এটি লিঙ্কারকে সি-র স্কিমটি ব্যবহার করতে বলে।

সংকলন / সংযোগের ক্ষেত্রে কীভাবে এটি আমাদের যে সমস্যার প্রয়োজন তা সমাধান করে?

সি নামকরণের স্কিম ব্যবহার করা আপনাকে সি-স্টাইলের প্রতীকগুলি উল্লেখ করতে দেয়। অন্যথায় লিঙ্কারটি সি ++ - শৈলীর চিহ্নগুলিতে চেষ্টা করবে যা কাজ করবে না।


7

আপনি সিআর ++ ফাইলটিতে ব্যবহৃত সি সংকলক দ্বারা সংকলিত একটি ফাইলে থাকা শিরোনাম সংজ্ঞায়িত ফাংশন অন্তর্ভুক্ত করার সময় যে কোনও সময় আপনি বাহ্যিক "সি" ব্যবহার করা উচিত। (অনেক স্ট্যান্ডার্ড সি লাইব্রেরিতে বিকাশকারীকে আরও সহজ করার জন্য এই চেকটি তাদের শিরোনামগুলিতে অন্তর্ভুক্ত থাকতে পারে)

উদাহরণস্বরূপ, যদি আপনার কাছে 3 টি ফাইল, ইউজন.সি., ইউএসকিউ, এবং মেইন.সি.পি এবং একটি .c এবং .cpp ফাইল উভয় সি ++ সংকলক (জি ++, সিসি, ইত্যাদি) দিয়ে সংকলিত হয় তবে তা 'নয় t সত্যিই প্রয়োজন, এবং এমনকি লিঙ্কার ত্রুটি হতে পারে। যদি আপনার বিল্ড প্রক্রিয়াটি ইউটি.সি. এর জন্য নিয়মিত সি সংকলক ব্যবহার করে, তবে আপনাকে ইউএস.কে অন্তর্ভুক্ত করার সময় বহিরাগত "সি" ব্যবহার করতে হবে you

যা হচ্ছে তা হ'ল সি ++ তার নামে ফাংশনটির প্যারামিটারগুলি এনকোড করে। এভাবেই ফাংশন ওভারলোডিং কাজ করে। সি ফাংশনে যা ঘটে থাকে তা হ'ল নামের শুরুতে আন্ডারস্কোর ("_") যুক্ত করা। বাহ্যিক "সি" ব্যবহার না করে লিঙ্কারটি ডোজোমিংথিং @@ ইন্ট @ ফ্লোট () নামক একটি ফাংশন সন্ধান করবে যখন ফাংশনের আসল নাম _ডোসোমিংথিং () বা কেবল ডসোমিংথিং () হয়।

বহিরাগত "সি" ব্যবহার করে উপরের সমস্যাটি সি ++ সংকলককে বলে যে এটি সি ++ এর পরিবর্তে সি নামকরণ কনভেনশন অনুসরণ করে এমন একটি ফাংশন সন্ধান করা উচিত telling


7

সি ++ সংকলক সি সংকলকটির চেয়ে পৃথকভাবে চিহ্নের নাম তৈরি করে। সুতরাং, আপনি যদি সি কোড হিসাবে সংকলিত সি ফাইলে থাকা কোনও ফাংশনে কল করার চেষ্টা করছেন, আপনাকে সি ++ সংকলকটি বলতে হবে যে প্রতীক নামগুলি এটির চেয়ে ডিফল্টর চেয়ে আলাদা দেখতে সমাধান করার চেষ্টা করছে; অন্যথায় লিঙ্ক পদক্ষেপ ব্যর্থ হবে।


6

extern "C" {}কনস্ট্রাক্ট নাম ধনুর্বন্ধনী মধ্যে ঘোষিত উপর mangling সঞ্চালন না করতে কম্পাইলার নির্দেশ দেয়। সাধারণত, সি ++ সংকলক ফাংশনটির নামগুলিকে "বাড়িয়ে তোলে" যাতে তারা আর্গুমেন্ট এবং রিটার্ন মান সম্পর্কে টাইপ তথ্য এনকোড করে; এটিকে ম্যাংলেড নাম বলা হয় । extern "C"কনস্ট্রাক্ট mangling বাধা দেয়।

এটি সাধারণত ব্যবহৃত হয় যখন সি ++ কোডের জন্য সি-ভাষার লাইব্রেরি কল করতে হয়। এটি সি ক্লায়েন্টগুলির কাছে সি ++ ফাংশন (উদাহরণস্বরূপ, কোনও ডিএলএল থেকে) এক্সপোজ করার সময়ও ব্যবহার করা যেতে পারে।


5

নাম ম্যাংলিংয়ের সমস্যাগুলি সমাধান করতে এটি ব্যবহার করা হয়। বাহ্যিক সি এর অর্থ হল যে ফাংশনগুলি একটি "ফ্ল্যাট" সি-স্টাইলের এপিআইতে রয়েছে।


0

g++কী চলছে তা দেখার জন্য একটি উত্পন্ন বাইনারি সাজান

কেন externপ্রয়োজনীয় তা বোঝার জন্য, সর্বোত্তম কাজটি হ'ল একটি উদাহরণ সহ অবজেক্ট ফাইলে কী কী বিশদ চলছে তা বোঝা:

main.cpp

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

জিসিসি 4.8 লিনাক্স ইএলএফ আউটপুট দিয়ে সংকলন করুন :

g++ -c main.cpp

প্রতীক টেবিলটি সাজান:

readelf -s main.o

আউটপুট রয়েছে:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  8: 0000000000000000     6 FUNC    GLOBAL DEFAULT    1 _Z1fv
  9: 0000000000000006     6 FUNC    GLOBAL DEFAULT    1 ef
 10: 000000000000000c    16 FUNC    GLOBAL DEFAULT    1 _Z1hv
 11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
 12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

ব্যাখ্যা

আমরা দেখতে পাচ্ছি:

  • efএবং egকোড হিসাবে একই নামের চিহ্ন সহ সংরক্ষণ করা হয়েছিল

  • অন্যান্য চিহ্নগুলি ম্যাঙ্গেল করা হয়েছিল। আসুন এগুলিকে অবিচ্ছিন্ন করুন:

    $ c++filt _Z1fv
    f()
    $ c++filt _Z1hv
    h()
    $ c++filt _Z1gv
    g()

উপসংহার: নিম্নলিখিত উভয় প্রতীক ধরণের ম্যাংগল ছিল না :

  • সংজ্ঞায়িত
  • Ndx = UNDলিঙ্কে সরবরাহ করা বা অন্য অবজেক্ট ফাইল থেকে রান সময় হিসাবে ঘোষিত তবে অপরিজ্ঞিত ( ) ঘোষণা করা হয়েছে

সুতরাং extern "C"কল করার সময় আপনার উভয়ের প্রয়োজন হবে :

  • সি ++ থেকে সি: g++উত্পাদিত অবিচ্ছিন্ন প্রতীকগুলি আশা করতে বলুনgcc
  • সি থেকে সি ++: ব্যবহারের g++জন্য অবিরাম চিহ্ন তৈরি করতে বলুনgcc

যে জিনিসগুলি বহিরাগত সি তে কাজ করে না

এটি সুস্পষ্ট হয়ে যায় যে যে কোনও সি ++ বৈশিষ্ট্য যাতে নাম ম্যাংলিংয়ের প্রয়োজন হয় তা ভিতরে কাজ করবে না extern C:

extern "C" {
    // Overloading.
    // error: declaration of C function ‘void f(int)’ conflicts with
    void f();
    void f(int i);

    // Templates.
    // error: template with C linkage
    template <class C> void f(C i) { }
}

সি ++ উদাহরণ থেকে ন্যূনতম চলমান সি

সম্পূর্ণতার জন্য এবং সেখানে নতুনদের জন্য, আরও দেখুন: একটি সি ++ প্রকল্পে সি উত্স ফাইলগুলি কীভাবে ব্যবহার করবেন?

সি ++ থেকে সি কল করা বেশ সহজ: প্রতিটি সি ফাংশনে কেবল একটি সম্ভাব্য অ-মাংগেল্ড প্রতীক থাকে, সুতরাং অতিরিক্ত কোনও কাজের প্রয়োজন হয় না।

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

সিএইচ

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

সিসি

#include "c.h"

int f(void) { return 1; }

চালান:

g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out

extern "C"লিঙ্ক ব্যতীত ব্যর্থ:

main.cpp:6: undefined reference to `f()'

কারণ g++একটি ম্যাঙ্গলেড f, যা gccউত্পাদন করেনি খুঁজে প্রত্যাশা করে।

গিটহাবের উপর উদাহরণ

সি উদাহরণ থেকে ন্যূনতম চলমান সি ++

সি ++ থেকে কল করা কিছুটা শক্ত: আমাদের প্রতিটি ফাংশন ম্যানুয়ালি তৈরি করতে হবে যা আমরা প্রকাশ করতে চাই।

এখানে আমরা চিত্রিত করি যে কীভাবে সি ++ ফাংশন ওভারলোডগুলি সি তে প্রকাশ করা যায় to

main.c

#include <assert.h>

#include "cpp.h"

int main(void) {
    assert(f_int(1) == 2);
    assert(f_float(1.0) == 3);
    return 0;
}

cpp.h

#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

#include "cpp.h"

int f(int i) {
    return i + 1;
}

int f(float i) {
    return i + 2;
}

int f_int(int i) {
    return f(i);
}

int f_float(float i) {
    return f(i);
}

চালান:

gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out

ছাড়া extern "C"এটি দিয়ে ব্যর্থ হলে:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

কারণ g++তৈরি ম্যাঙ্গেলড প্রতীক যা gccখুঁজে পায় না।

গিটহাবের উপর উদাহরণ

উবুন্টুতে পরীক্ষিত 18.04।


1
ডাউনভোটটি ব্যাখ্যা করার জন্য ধন্যবাদ, এটি এখন সমস্ত অর্থবোধ করে।
সিরো সান্তিলি 郝海东 冠状 病 六四 事件 法轮功
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.