C ++ में बाहरी "C" का क्या प्रभाव है?


1629

वास्तव extern "C"में C ++ कोड में क्या करता है?

उदाहरण के लिए:

extern "C" {
   void foo();
}

82
मैं आपको इस लेख से परिचित कराना चाहता हूं: http://www.agner.org/optimize/calling_conventions.pdf यह आपको अधिवेशन बुलाने और कंपाइलरों के बीच अंतर के बारे में बहुत कुछ बताता है।
सैम लियाओ

1
@Litherum मेरे सिर के शीर्ष पर, यह संकलक को C का उपयोग करके कोड के उस दायरे को संकलित करने के लिए कह रहा है, यह देखते हुए कि आपके पास एक क्रॉस-कंपाइलर है। इसके अलावा, इसका मतलब है कि आपके पास एक Cpp फ़ाइल है जहां आपके पास वह foo()फ़ंक्शन है।
ha9u63ar

1
@ ha9u63ar यह 'मेरे सिर के ऊपर' है। आपकी शेष टिप्पणी भी गलत है। मैं आपको इसे हटाने की सलाह देता हूं।
TamaMcGlinn

जवाबों:


1555

extern "C" C ++ में एक फंक्शन-नेम बनाता है, जिसमें 'C' लिंकेज होता है (कंपाइलर नाम नहीं बताता है) ताकि क्लाइंट C कोड 'C' संगत हेडर फाइल का उपयोग करके आपके फंक्शन को लिंक कर सके (यानी उपयोग कर सके) जिसमें सिर्फ 'सी' हो अपने कार्य की घोषणा। आपकी फ़ंक्शन परिभाषा एक बाइनरी प्रारूप में सम्‍मिलित है (जो आपके C ++ कंपाइलर द्वारा संकलित की गई थी) कि क्लाइंट 'C' लिंकर फिर 'C' नाम का उपयोग करने के लिए लिंक करेगा।

चूंकि C ++ में फ़ंक्शन नामों का ओवरलोडिंग है और C नहीं है, इसलिए C ++ कंपाइलर फ़ंक्शन नाम को लिंक करने के लिए एक अद्वितीय आईडी के रूप में उपयोग नहीं कर सकता है, इसलिए यह तर्कों के बारे में जानकारी जोड़कर नाम का प्रबंधन करता है। AC कंपाइलर को नाम को मेन्यू करने की आवश्यकता नहीं है क्योंकि आप C. में फ़ंक्शन नामों को अधिभार नहीं दे सकते हैं। जब आप कहते हैं कि एक फ़ंक्शन C ++ में बाहरी "C" लिंकेज है, तो C ++ कंपाइलर उपयोग किए गए नाम के लिए तर्क / पैरामीटर प्रकार की जानकारी नहीं जोड़ता है। लिंकेज।

बस इतना पता है, आप स्पष्ट रूप से प्रत्येक व्यक्ति घोषणा / परिभाषा के लिए "सी" लिंकेज निर्दिष्ट कर सकते हैं या एक निश्चित लिंकेज करने के लिए घोषणाओं / परिभाषाओं के अनुक्रम को समूह के लिए ब्लॉक का उपयोग कर सकते हैं:

extern "C" void foo(int);
extern "C"
{
   void g(char);
   int i;
}

यदि आप तकनीकी के बारे में परवाह करते हैं, तो वे सी ++ 03 मानक के खंड 7.5 में सूचीबद्ध हैं, यहां एक संक्षिप्त सारांश है (बाहरी "सी" पर जोर देने के साथ):

  • बाहरी "C" एक लिंकेज-विनिर्देश है
  • हर संकलक को "सी" लिंकेज प्रदान करना आवश्यक है
  • लिंकेज विनिर्देश केवल नाम स्थान के दायरे में होगा
  • सभी फ़ंक्शन प्रकार, फ़ंक्शन नाम और चर नामों में एक भाषा लिंकेज है रिचर्ड की टिप्पणी देखें: केवल फ़ंक्शन नाम और बाहरी लिंकेज वाले चर नामों में एक भाषा लिंकेज है
  • अलग-अलग भाषा लिंकेज वाले दो फ़ंक्शन प्रकार अलग-अलग प्रकार के होते हैं, भले ही समान हों
  • लिंकेज चश्मा घोंसला, आंतरिक एक अंतिम लिंकेज निर्धारित करता है
  • बाहरी "सी" को वर्ग के सदस्यों के लिए अनदेखा किया जाता है
  • किसी विशेष नाम के साथ एक फ़ंक्शन में "C" लिंकेज (नाम स्थान की परवाह किए बिना) हो सकता है
  • बाहरी "C" एक फ़ंक्शन को बाहरी संबंध रखने के लिए मजबूर करता है (इसे स्थिर नहीं बना सकता है) रिचर्ड की टिप्पणी देखें: ' बाहरी "C" के अंदर' स्थिर 'वैध है; घोषित की गई एक इकाई में आंतरिक लिंकेज है, और इसलिए एक भाषा लिंकेज नहीं है
  • सी ++ से अन्य भाषाओं में परिभाषित वस्तुओं और अन्य भाषाओं से सी ++ में परिभाषित वस्तुओं का कार्यान्वयन कार्यान्वयन-परिभाषित और भाषा-निर्भर है। केवल जहां दो भाषा कार्यान्वयन की ऑब्जेक्ट लेआउट रणनीतियाँ समान हैं, ऐसे लिंक को प्राप्त किया जा सकता है

21
C कंपाइलर mangling का उपयोग नहीं करता है जो c ++ का है। इसलिए यदि आप एक c ++ प्रोग्राम से कॉल इंटरफ़ेस चाहते हैं, तो आपको स्पष्ट रूप से घोषित करना होगा कि c इंटरफ़ेस "extern c" है।
सैम लियाओ

58
@Faisal: विभिन्न C ++ कंपाइलर के साथ निर्मित कोड को लिंक करने का प्रयास न करें, भले ही क्रॉस-रेफरेंस सभी 'एक्सटर्नल "C" हों।' कक्षाओं के लेआउट के बीच अक्सर अंतर होते हैं, या अपवादों को संभालने के लिए उपयोग किए जाने वाले तंत्र, या उपयोग करने से पहले चर सुनिश्चित करने के लिए उपयोग किए जाने वाले तंत्र, या ऐसे अन्य मतभेद हैं, साथ ही आपको दो अलग-अलग C ++ रन-टाइम समर्थन पुस्तकालयों (एक के लिए) की आवश्यकता हो सकती है प्रत्येक संकलक)।
जोनाथन लेफलर

8
'बाहरी "C" फ़ंक्शन को बाहरी लिंकेज के लिए मजबूर करता है (इसे स्थिर नहीं बना सकता)' गलत है। '' बाहरी '' C '' के अंदर 'स्थिर' वैध है; घोषित की गई एक इकाई में आंतरिक लिंकेज है, और इसलिए एक भाषा लिंकेज नहीं है।
रिचर्ड स्मिथ

14
'सभी फ़ंक्शन प्रकार, फ़ंक्शन नाम और चर नाम में एक भाषा लिंकेज है' भी गलत है। केवल फ़ंक्शन नाम और बाहरी लिंकेज वाले चर नामों में एक भाषा लिंकेज है।
रिचर्ड स्मिथ

9
ध्यान दें कि extern "C" { int i; }एक परिभाषा है। यह वह नहीं हो सकता है जिसका आपने इरादा किया था, की गैर-परिभाषा के बगल में void g(char);। इसे एक गैर-परिभाषा बनाने के लिए, आपको आवश्यकता होगी extern "C" { extern int i; }। दूसरी ओर, ब्रेसिज़ के बिना एक-घोषणा सिंटैक्स घोषणा को एक गैर-परिभाषा extern "C" int i;extern "C" { extern int i; }
बनाता

327

बस थोड़ी जानकारी जोड़ना चाहता था, क्योंकि मैंने इसे अभी तक पोस्ट नहीं किया है।

आप बहुत बार सी हेडर में कोड देखेंगे जैसे:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

यह क्या पूरा करता है यह आपको अपने सी ++ कोड के साथ उस सी हेडर फ़ाइल का उपयोग करने की अनुमति देता है, क्योंकि मैक्रो "__cplusplus" को परिभाषित किया जाएगा। लेकिन आप अभी भी इसे अपनी विरासत सी कोड के साथ उपयोग कर सकते हैं , जहां मैक्रो को परिभाषित नहीं किया गया है, इसलिए यह विशिष्ट सी ++ निर्माण नहीं देखेगा।

हालाँकि, मैंने C ++ कोड भी देखा है जैसे:

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

जिसकी मैं कल्पना करता हूं, वही बात पूरी करती है।

निश्चित नहीं है कि कौन सा तरीका बेहतर है, लेकिन मैंने दोनों को देखा है।


11
एक अलग अंतर है। पूर्व के मामले में, यदि आप इस फ़ाइल को सामान्य gcc संकलक के साथ संकलित करते हैं, तो यह एक ऐसी वस्तु उत्पन्न करेगा जहाँ फ़ंक्शन नाम नहीं है। यदि आप तब लिंक के साथ C और C ++ ऑब्जेक्ट लिंक करते हैं तो यह फ़ंक्शन नहीं ढूंढेगा। आपको बाहरी कोड के साथ उन "लीगेसी हेडर" फाइलों को अपने दूसरे कोड ब्लॉक में शामिल करना होगा।
ऐनी वैन रोसुम

8
@Anne: C ++ कंपाइलर अनमैन्गल नामों की तलाश करेगा, क्योंकि यह extern "C"हेडर में देखा गया है)। यह महान काम करता है, इस तकनीक का कई बार उपयोग किया है।
बेन वोइगट

20
@Anne: यह सही नहीं है, पहले वाला ठीक भी है। इसे C कंपाइलर द्वारा अनदेखा किया जाता है, और C ++ में दूसरे के समान प्रभाव पड़ता है। कंपाइलर कम परवाह नहीं कर सकता है कि क्या यह extern "C"हेडर को शामिल करने से पहले या बाद में सामना करता है। जब तक यह संकलक तक पहुंचता है, तब तक यह प्रीप्रोसेड पाठ की सिर्फ एक लंबी धारा है।
बेन वोइगट

8
@ कोई, नहीं, मुझे लगता है कि आप स्रोत में किसी अन्य त्रुटि से प्रभावित हुए हैं, क्योंकि आप जो वर्णन कर रहे हैं वह गलत है। का कोई संस्करण g++कम से कम पिछले 17 साल में यह गलत है, किसी भी लक्ष्य के लिए, किसी भी समय। पहले उदाहरण का पूरा बिंदु यह है कि इससे कोई फर्क नहीं पड़ता कि आप C या C ++ कंपाइलर का उपयोग करते हैं, extern "C"ब्लॉक में नामों के लिए कोई नाम नहीं लिया जाएगा ।
जोनाथन वेकली

7
"कौन सा बेहतर है" - निश्चित रूप से, पहला संस्करण बेहतर है: यह हेडर को सीधे अनुमति देता है, सी और सी ++ कोड दोनों में, किसी भी आगे की आवश्यकताओं को पूरा करता है। दूसरा दृष्टिकोण सी हेडर के लिए एक वर्कअराउंड है लेखक सी ++ गार्ड को भूल गया (कोई समस्या नहीं है, हालांकि, अगर बाद में इन्हें जोड़ा जाता है, तो नेस्टेड बाहरी "सी" घोषणाएं स्वीकार की जाती हैं ...)।
एकनकागुआ

267

g++क्या हो रहा है यह देखने के लिए एक उत्पन्न बाइनरी को डिकम्पाइल करें

main.cpp

void f() {}
void g();

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

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

संकलित और उत्पन्न ELF आउटपुट को अलग करें :

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

आउटपुट में शामिल हैं:

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 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"कॉल करते समय दोनों की आवश्यकता होगी :

  • C ++ से C: g++द्वारा उत्पादित अनमैंगल्ड प्रतीकों की अपेक्षा करनाgcc
  • C ++ से C: उपयोग करने के g++लिए अनमैन्डल प्रतीक उत्पन्न करना बताएंgcc

बाहरी सी में काम नहीं करने वाली चीजें

यह स्पष्ट हो जाता है कि किसी भी C ++ सुविधा के लिए जिसे नाम की आवश्यकता है, अंदर काम नहीं करेगी 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) { }
}

C ++ उदाहरण से न्यूनतम रनवेबल C

पूर्णता के लिए और वहाँ से बाहर के newbs के लिए, यह भी देखें: C ++ प्रोजेक्ट में C स्रोत फ़ाइलों का उपयोग कैसे करें?

C ++ से C को कॉल करना बहुत आसान है: प्रत्येक C फ़ंक्शन में केवल एक संभव गैर-मैनगल्ड प्रतीक है, इसलिए किसी अतिरिक्त कार्य की आवश्यकता नहीं है।

main.cpp

#include <cassert>

#include "c.h"

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

ch

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

सीसी

#include "c.h"

int f(void) { return 1; }

Daud:

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उत्पन्न नहीं हुई।

GitHub पर उदाहरण

C के उदाहरण से न्यूनतम रन योग्य C ++

C से C ++ को कॉल करना थोड़ा कठिन है: हमें प्रत्येक फ़ंक्शन के गैर-मैंगल्ड संस्करण को मैन्युअल रूप से बनाना होगा जिसे हम उजागर करना चाहते हैं।

यहाँ हम बताते हैं कि C ++ फंक्शन ओवरलोड को C में कैसे एक्सपोज़ किया जाए।

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

Daud:

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नहीं मिल सकते हैं।

GitHub पर उदाहरण

उबुन्टु 18.04 में परीक्षण किया गया।


21
1 उत्तर के बाद से आप 1) स्पष्ट रूप से उल्लेख करते हैं कि extern "C" {आपको C ++ प्रोग्रामों के भीतर से अनमैन्डल सी फ़ंक्शन को कॉल करने में मदद करता है , साथ ही सी प्रोग्राम्स के भीतर से अनमैन्डल सी ++ फ़ंक्शंस , जो अन्य उत्तर इतने स्पष्ट नहीं हैं, और 2) क्योंकि आप अलग-अलग उदाहरण दिखाते हैं से प्रत्येक। धन्यवाद!
गेब्रियल स्टेपल्स

3
मुझे यह उत्तर बहुत पसंद है
आत्मबूट

4
सबसे अच्छा जवाब हाथ से दें क्योंकि यह दिखाता है कि
गैसपाउ

1
@JaveneCPPMcGowan आपको क्या लगता है कि मेरे पास C ++ शिक्षक थे? :-)
सिरो सेंटिल्ली 冠状 C ill ill 法轮功 ''

205

प्रत्येक C ++ प्रोग्राम में, सभी गैर-स्थिर कार्यों को बाइनरी फ़ाइल में प्रतीकों के रूप में दर्शाया गया है। ये प्रतीक विशेष पाठ तार हैं जो विशिष्ट रूप से कार्यक्रम में एक समारोह की पहचान करते हैं।

सी में, प्रतीक नाम फ़ंक्शन नाम के समान है। यह संभव है क्योंकि C में दो गैर-स्थैतिक कार्यों का एक ही नाम नहीं हो सकता है।

क्योंकि C ++ ओवरलोडिंग की अनुमति देता है और इसमें कई विशेषताएं हैं जो C नहीं करता है - जैसे कक्षाएं, सदस्य फ़ंक्शन, अपवाद विनिर्देशों - यह केवल फ़ंक्शन नाम का उपयोग प्रतीक नाम के रूप में करना संभव नहीं है। इसे हल करने के लिए, C ++ तथाकथित नाम मैनलिंग का उपयोग करता है, जो फ़ंक्शन नाम और सभी आवश्यक जानकारी (जैसे तर्कों की संख्या और आकार) को कुछ अजीब दिखने वाले स्ट्रिंग में केवल संकलक और लिंकर द्वारा संसाधित करता है।

इसलिए यदि आप किसी फ़ंक्शन को एक्सटर्नल C होने के लिए निर्दिष्ट करते हैं, तो कंपाइलर उसके साथ नाम जुड़ने का कार्य नहीं करता है और इसे फ़ंक्शन नाम के रूप में इसके प्रतीक नाम का उपयोग करके सीधे एक्सेस किया जा सकता है।

इस तरह के कार्यों का उपयोग करने dlsym()और dlopen()कॉल करने के दौरान यह काम आता है।


तुम काम से क्या मतलब है? प्रतीक का नाम है = फ़ंक्शन का नाम प्रतीक नाम को dlsym के पास दिया जाएगा, या अन्य चीज़?
एरर

1
@ विदेश: हाँ। यह सामान्य रूप से असंभव है कि केवल एक हेडर फ़ाइल दी गई dlopen () C ++ साझा लाइब्रेरी को लोड करने के लिए सही फ़ंक्शन चुनें। (X86 पर, इटेनियम एबीआई के रूप में एक प्रकाशित नाम-मैनिंग विनिर्देश है जिसे सभी x86 कंपाइलर मुझे C ++ फ़ंक्शन नामों को उपयोग करने के लिए जानते हैं, लेकिन भाषा में कुछ भी इसकी आवश्यकता नहीं है।)
जोनाथन तोमर

52

C ++ एक प्रक्रियात्मक भाषा से ऑब्जेक्ट-ओरिएंटेड भाषा बनाने के लिए फ़ंक्शन का नाम देता है

अधिकांश प्रोग्रामिंग भाषाओं को मौजूदा प्रोग्रामिंग भाषाओं के शीर्ष पर नहीं बनाया गया है। C ++ C के ऊपर बनाया गया है, और इसके अलावा यह एक प्रक्रिया-आधारित प्रोग्रामिंग भाषा से निर्मित एक वस्तु-उन्मुख प्रोग्रामिंग भाषा है, और इस कारण से C ++ के भाव हैं जैसे extern "C"कि C के साथ पीछे की संगतता प्रदान करते हैं।

आइए निम्नलिखित उदाहरण देखें:

#include <stdio.h>

// Two functions are defined with the same name
// but have different parameters

void printMe(int a) {
  printf("int: %i\n", a);
}

void printMe(char a) {
  printf("char: %c\n", a);
}

int main() {
  printMe("a");
  printMe(1);
  return 0;
}

एसी कंपाइलर उपरोक्त उदाहरण को संकलित नहीं करेगा, क्योंकि एक ही फ़ंक्शन printMeको दो बार परिभाषित किया जाता है (भले ही उनके अलग-अलग पैरामीटर int aबनाम हों char a)।

gcc -o printMe PrintMe.c &&/printMe;
1 त्रुटि। PrintMe को एक से अधिक बार परिभाषित किया गया है।

एक C ++ कंपाइलर उपरोक्त उदाहरण को संकलित करेगा। यह परवाह नहीं करता है कि printMeदो बार परिभाषित किया गया है।

g ++ -o PrintMe PrintMe.c &&/printMe;

इसका कारण यह है कि C ++ कंपाइलर अपने मापदंडों के आधार पर अनुमानित नाम ( मंगल ) फ़ंक्शन करता है। सी में, यह सुविधा समर्थित नहीं थी। हालाँकि, जब C ++ C के ऊपर बनाया गया था, तो भाषा को ऑब्जेक्ट-ओरिएंटेड डिज़ाइन किया गया था, और एक ही नाम के तरीकों (कार्यों) के साथ अलग-अलग कक्षाएं बनाने की क्षमता का समर्थन करने की आवश्यकता थी, और अलग-अलग तरीकों ( ओवरराइडिंग ) के आधार पर अलग- अलग करने की क्षमता मापदंडों।

extern "C" कहते हैं, "सी फ़ंक्शन के नाम को कम न करें"

हालाँकि, कल्पना करें कि हमारे पास "parent.c" नाम की एक विरासत सी फ़ाइल है, जो includeअन्य विरासत C फ़ाइलों, "parent.h", "child.h", आदि से नामों का कार्य करती है। यदि विरासत "parent.c" फ़ाइल चलती है C ++ कंपाइलर के माध्यम से, फिर फंक्शन के नामों को मंगवा लिया जाएगा, और वे अब "parent.h", "child.h", आदि में निर्दिष्ट फ़ंक्शन नामों से मेल नहीं खाएंगे - इसलिए उन बाहरी फ़ाइलों में फ़ंक्शन के नामों की भी आवश्यकता होगी मंगली होना। एक जटिल सी प्रोग्राम में मंगलिंग फ़ंक्शन के नाम, बहुत सारी निर्भरता वाले लोग, टूटे हुए कोड को जन्म दे सकते हैं; इसलिए यह एक कीवर्ड प्रदान करने के लिए सुविधाजनक हो सकता है जो C ++ कंपाइलर को फ़ंक्शन नाम का उपयोग न करने के लिए कह सकता है।

extern "C"कीवर्ड एक सी ++ संकलक बताता को वध करना (नाम बदलने) सी समारोह नहीं नाम।

उदाहरण के लिए:

extern "C" void printMe(int a);


extern "C"अगर हमारे पास सिर्फ एक dllफाइल है तो क्या हम उसका उपयोग नहीं कर सकते ? मेरा मतलब है कि अगर हमारे पास हेडर फ़ाइल नहीं है और सिर्फ एक स्रोत फ़ाइल (सिर्फ कार्यान्वयन) है और फ़ंक्शन पॉइंटर के माध्यम से इसके फ़ंक्शन का उपयोग किया जाता है। इस अवस्था में, हम केवल फ़ंक्शन का उपयोग करते हैं (इसके नाम की परवाह किए बिना)।
BattleTested

@tfmontague, मेरे लिए आपने इसे सही ठहराया है! सीधे सिर में।
आर्टानिस ज़ेरतुल

29

एक्सटर्नल "C" में केवल रैप करके किसी भी C- हेडर को C ++ के साथ संगत नहीं बनाया जा सकता है। C ++ कीवर्ड के साथ C- हेडर संघर्ष में पहचानकर्ता जब C ++ कंपाइलर इस बारे में शिकायत करेंगे।

उदाहरण के लिए, मैंने निम्न कोड को g ++ में विफल देखा है:

extern "C" {
struct method {
    int virtual;
};
}

किंडा समझ में आता है, लेकिन सी-कोड को सी ++ में पोर्ट करते समय कुछ ध्यान में रखना है।


14
extern "C"सी लिंकेज का उपयोग करने का मतलब है, जैसा कि अन्य उत्तरों द्वारा वर्णित है। इसका मतलब यह नहीं है कि "सामग्री को सी" या कुछ भी संकलित करें। int virtual;C ++ में अमान्य है और विभिन्न लिंकेज निर्दिष्ट करने से वह परिवर्तित नहीं होता है।
एमएम

1
... या आम तौर पर, सिंटैक्स त्रुटि वाले किसी भी कोड को संकलित नहीं किया जाएगा।
वैलेंटाइन हेनित्ज

4
@ValentinHeinitz स्वाभाविक रूप से, हालांकि C में एक पहचानकर्ता के रूप में "आभासी" का उपयोग करना वाक्यविन्यास त्रुटि नहीं है। मैं केवल यह बताना चाहता था कि आप इसके चारों ओर "C" लगाकर C ++ में किसी भी C हेडर का उपयोग स्वतः नहीं कर सकते ।
सैंडर मर्टेंस

28

यह एक फ़ंक्शन के लिंकेज को इस तरह से बदलता है कि फ़ंक्शन सी से कॉल करने योग्य है। व्यवहार में इसका मतलब है कि फ़ंक्शन का नाम मैनडाउन नहीं है ।


3
मंगली शब्द आमतौर पर इस्तेमाल किया जाने वाला शब्द है ... विश्वास न करें कि मैंने कभी इस अर्थ के साथ 'सजाया' देखा है।
मैथ्यू शारले

1
Microsoft (कम से कम आंशिक रूप से) उनके प्रलेखन में घिरे होने के बजाय सजे हुए का उपयोग करता है । वे अपने उपकरण का नाम अनिर्धारित (उर्फ अन-मनगल) भी रखते हैं undname
रेने न्यफेनेगर

20

यह सी + + कंपाइलर को लिंक करते समय सी-स्टाइल में उन कार्यों के नामों को देखने के लिए सूचित करता है, क्योंकि लिंकिंग चरण के दौरान सी और सी ++ में संकलित कार्यों के नाम अलग-अलग हैं।


12

बाहरी "C" का अर्थ C ++ कंपाइलर द्वारा पहचाना जाना और कंपाइलर को सूचित करना है कि विख्यात फ़ंक्शन C शैली में संकलित (या होना है) है। ताकि लिंक करते समय, यह C से फ़ंक्शन के सही संस्करण से लिंक हो।


6

मैंने dll (डायनामिक लिंक लाइब्रेरी) फ़ाइलों को मुख्य () फंक्शन "एक्सपोर्टेबल" बनाने के लिए 'extern "C" का उपयोग किया है, इसलिए इसका उपयोग बाद में dll से अन्य निष्पादन योग्य में किया जा सकता है। शायद इसका एक उदाहरण जहां मैं इसका उपयोग करता था वह उपयोगी हो सकता है।

DLL

#include <string.h>
#include <windows.h>

using namespace std;

#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
    MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}

प्रोग्राम फ़ाइल

#include <string.h>
#include <windows.h>

using namespace std;

typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder

int main()
{
    char winDir[MAX_PATH];//will hold path of above dll
    GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
    strcat(winDir,"\\exmple.dll");//concentrate dll name with path
    HINSTANCE DLL = LoadLibrary(winDir);//load example dll
    if(DLL==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if load fails exit
        return 0;
    }
    mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
    //defined variable is used to assign a function from dll
    //GetProcAddress is used to locate function with pre defined extern name "DLL"
    //and matcing function name
    if(mainDLLFunc==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if it fails exit
        return 0;
    }
    mainDLLFunc();//run exported function 
    FreeLibrary((HMODULE)DLL);
}

4
जाली। extern "C"और __declspec(dllexport)असंबंधित हैं। पूर्व नियंत्रण प्रतीक सजावट को नियंत्रित करता है, बाद वाला निर्यात प्रविष्टि बनाने के लिए जिम्मेदार है। आप C ++ नाम की सजावट का उपयोग करके एक प्रतीक का निर्यात कर सकते हैं। इस प्रश्न के बिंदु को पूरी तरह से गायब करने के अलावा, कोड नमूने में अन्य गलतियाँ भी हैं। एक के लिए, mainआपके DLL से निर्यात किया गया वापसी मूल्य घोषित नहीं करता है। या उस मामले के लिए सम्मेलन बुला रहा है। आयात करते समय, आप एक यादृच्छिक कॉलिंग कन्वेंशन ( WINAPI) का उपयोग करते हैं, और 32-बिट बिल्ड (होना चाहिए _mainया _main@0) के लिए गलत प्रतीक का उपयोग करें । क्षमा करें, -1।
IInspectable

1
यह केवल दोहराया गया है, कि आप नहीं जानते कि आप क्या कर रहे हैं, लेकिन इस तरह से करना आपके लिए काम करने के लिए प्रकट होता है, लक्ष्य प्लेटफार्मों की कुछ अज्ञात सूची के लिए। आपने अपनी पिछली टिप्पणी में मेरे द्वारा उठाए गए मुद्दों को संबोधित नहीं किया। यह अभी भी एक डाउन-वोट है, बेतहाशा गलत होने के कारण (यह अधिक है, जो एक टिप्पणी में फिट नहीं हुआ)।
IInspectable

1
स्टैक ओवरफ्लो के प्रकार पर एक उत्तर पोस्ट करने का तात्पर्य है, कि आप जानते हैं कि आप क्या कर रहे हैं। यह अपेक्षित है। आपके प्रयास के अनुसार "रन पर ढेर भ्रष्टाचार को रोकने के लिए" : आपका फ़ंक्शन हस्ताक्षर प्रकार का वापसी मूल्य निर्दिष्ट void*करता है, लेकिन आपका कार्यान्वयन कुछ भी वापस नहीं करता है। यह वास्तव में अच्छी तरह से उड़ जाएगा ...
IInspectable

1
आप कुछ है कि लागू करते हैं तो दिखाई देता है काम करने के लिए, शुद्ध भाग्य से है, तो आप स्पष्ट रूप से है नहीं पता है कि तुम क्या कर रहे हैं (अपने "काम" नमूना है कि श्रेणी में आता है)। यह अपरिभाषित व्यवहार है, और काम करने के लिए प्रकट होना अपरिभाषित व्यवहार का एक वैध रूप है। यह अभी भी अपरिभाषित है। यदि आप भविष्य में अधिक परिश्रम का अभ्यास करेंगे, तो मैं इसकी बहुत सराहना करूंगा। इसका एक हिस्सा इस प्रस्तावित उत्तर को हटा सकता है।
IInspectable

1
आप एक ऐसे फ़ंक्शन को पुनः स्थापित कर रहे हैं जो एक फ़ंक्शन के रूप में कुछ भी नहीं लौटाता है जो एक पॉइंटर लौटाता है। यह शुद्ध भाग्य है, कि x86 बहुत ही क्षमाशील है, जो समारोह के हस्ताक्षरों को मिसमैच करने के लिए और विशेष रूप से अभिन्न प्रकार के रिटर्न वैल्यू के संबंध में है। आपका कोड केवल संयोग से काम करता है। यदि आप असहमत हैं, तो आपको यह समझाने की आवश्यकता है कि आपका कोड मज़बूती से क्यों काम करता है।
IInspectable

5

extern "C"एक लिंकेज विनिर्देश है जिसका उपयोग C फंक्शन सोर्स C फ़ाइलों में कॉल करने के लिए किया जाता है । हम C फ़ंक्शन को कॉल कर सकते हैं , वेरिएबल्स लिख सकते हैं , और हेडर शामिल कर सकते हैं । समारोह बाहरी इकाई में घोषित किया गया है और इसे बाहर परिभाषित किया गया है। सिंटेक्स है

श्रेणी 1:

extern "language" function-prototype

टाइप 2:

extern "language"
{
     function-prototype
};

उदाहरण के लिए:

#include<iostream>
using namespace std;

extern "C"
{
     #include<stdio.h>    // Include C Header
     int n;               // Declare a Variable
     void func(int,int);  // Declare a function (function prototype)
}

int main()
{
    func(int a, int b);   // Calling function . . .
    return 0;
}

// Function definition . . .
void func(int m, int n)
{
    //
    //
}

3

यह उत्तर अधीर के लिए है / मिलने की समय सीमा है, केवल एक भाग / सरल विवरण नीचे है:

  • C ++ में, आप ओवरलोडिंग के माध्यम से कक्षा में एक ही नाम रख सकते हैं (उदाहरण के लिए, चूंकि वे सभी समान नाम निर्यात नहीं किए जा सकते हैं-dll, आदि से) इन समस्याओं के समाधान के लिए उन्हें अलग-अलग तारों में परिवर्तित किया जाता है (जिन्हें प्रतीक कहा जाता है) ), प्रतीक फ़ंक्शन के नाम को भी तर्क देते हैं, इसलिए इनमें से प्रत्येक फ़ंक्शन समान नाम के साथ भी, विशिष्ट रूप से पहचाना जा सकता है (इसे नाम भी कहा जाता है)
  • सी में, आपके पास ओवरलोडिंग नहीं है, फ़ंक्शन नाम अद्वितीय है (इसलिए, फ़ंक्शन नाम की विशिष्ट पहचान के लिए एक अलग स्ट्रिंग की आवश्यकता नहीं है, इसलिए प्रतीक फ़ंक्शन नाम ही है)

इसलिए
C ++ में, प्रत्येक फ़ंक्शन को विशिष्ट रूप से पहचानने वाले नाम के साथ
को विशिष्ट रूप से पहचानने वाले नाम के साथ, C में को विशिष्ट पहचान के नाम के बिना भी C में

C ++ के व्यवहार को बदलने के लिए, अर्थात्, यह निर्दिष्ट करने के लिए कि नामकरण किसी विशेष कार्य के लिए नहीं होना चाहिए , आप फ़ंक्शन के नाम से पहले "C" का उपयोग कर सकते हैं , जो भी कारण हो, जैसे dll से एक विशिष्ट नाम के साथ फ़ंक्शन निर्यात करना। , अपने ग्राहकों द्वारा उपयोग के लिए।

अधिक विस्तृत / अधिक सही उत्तरों के लिए अन्य उत्तर पढ़ें।


1

C और C ++ को मिलाते समय (यानी, A, C फ़ंक्शन को C ++ से; और b को C से C ++ फ़ंक्शन को मिलाते हुए), C ++ नाम की मैनलिंग समस्याओं को जोड़ने का कारण बनती है। तकनीकी रूप से, यह समस्या केवल तब होती है जब कैली फ़ंक्शन पहले से कंपाइलर का उपयोग करके द्विआधारी (सबसे अधिक संभावना है, * .A लाइब्रेरी फ़ाइल) में संकलित किया गया है।

इसलिए हमें C ++ में नामकरण को अक्षम करने के लिए बाहरी "C" का उपयोग करने की आवश्यकता है।


0

अन्य अच्छे उत्तरों के साथ संघर्ष किए बिना, मैं अपने उदाहरण का एक सा जोड़ दूंगा।

वास्तव में C ++ कंपाइलर क्या करता है: यह संकलन प्रक्रिया में नामों का प्रबंधन करता है, इसलिए हमें संकलक को विशेष रूप से कार्यान्वयन का इलाज C करने की आवश्यकता है ।

जब हम C ++ क्लासेस बना रहे हैं और जोड़ extern "C"रहे हैं, तो हम अपने C ++ कंपाइलर को बता रहे हैं कि हम C कॉलिंग कन्वेंशन का उपयोग कर रहे हैं।

कारण (हम C ++ से C कार्यान्वयन कह रहे हैं): या तो हम C फ़ंक्शन को C ++ से कॉल करना चाहते हैं या C ++ फ़ंक्शन को C (C ++ वर्ग ... आदि) C से काम नहीं करना चाहते हैं।


ढेर अतिप्रवाह में आपका स्वागत है। यदि आप एक पुराने प्रश्न का उत्तर देने का निर्णय लेते हैं, जो अच्छी तरह से स्थापित और सही उत्तर देता है, तो दिन में देर से एक नया उत्तर जोड़ने से आपको कोई क्रेडिट नहीं मिल सकता है। यदि आपके पास कुछ विशिष्ट नई जानकारी है, या आप आश्वस्त हैं कि अन्य उत्तर सभी गलत हैं, तो सभी तरीकों से एक नया उत्तर जोड़ सकते हैं, लेकिन 'अभी तक एक और उत्तर' एक ही मूल जानकारी देने के बाद प्रश्न पूछे जाने पर लंबे समय तक जीता है ' टी आप बहुत क्रेडिट कमाते हैं। सच कहूँ तो, मुझे नहीं लगता कि इस उत्तर में कुछ नया है।
जोनाथन लेफ़लर

वैसे मुझे आपकी बात याद रखनी चाहिए - ठीक है
सुशोभन दास

-1

C संकलक द्वारा संकलित एक फ़ंक्शन v (f) और C ++ संकलक द्वारा संकलित समान नाम v (f) के साथ एक फ़ंक्शन समान फ़ंक्शन नहीं है। यदि आपने उस फ़ंक्शन को C में लिखा है, और फिर आपने उसे C ++ से कॉल करने का प्रयास किया, तो लिंकर C ++ फ़ंक्शन की तलाश करेगा और C फ़ंक्शन को नहीं खोजेगा।

extern "C" C ++ कंपाइलर को बताता है कि आपके पास एक फ़ंक्शन है जो C कंपाइलर द्वारा संकलित किया गया था। एक बार जब आप यह बता देते हैं कि यह सी कंपाइलर द्वारा संकलित किया गया था, तो सी ++ कंपाइलर को पता चल जाएगा कि इसे सही तरीके से कैसे कॉल किया जाए।

यह C ++ कंपाइलर को C ++ फ़ंक्शन को इस तरह से संकलित करने की अनुमति देता है कि C कंपाइलर इसे कॉल कर सके। यह फ़ंक्शन आधिकारिक तौर पर एक C फ़ंक्शन होगा, लेकिन चूंकि यह C ++ कंपाइलर द्वारा संकलित किया गया है, इसलिए यह सभी C ++ सुविधाओं का उपयोग कर सकता है और इसमें सभी C ++ कीवर्ड हैं।


C ++ कंपाइलर एक extern "C"फ़ंक्शन को संकलित कर सकता है - और (कुछ बाधाओं के अधीन) यह C कंपाइलर द्वारा संकलित कोड द्वारा कॉल करने योग्य होगा।
जोनाथन लेफ़लर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.