C ++ और C को मिलाकर #ifdef __cplusplus कैसे काम करता है?


319

मैं एक ऐसी परियोजना पर काम कर रहा हूं जिसमें बहुत सी विरासत सी कोड है। हमने सी ++ में लिखना शुरू कर दिया है, आखिरकार विरासत कोड को बदलने के इरादे से, साथ ही साथ। मैं थोड़ा उलझन में हूं कि सी और सी ++ कैसे बातचीत करते हैं। मैं रैप करके समझते हैं कि सी के साथ कोड extern "C"C ++ कम्पाइलर वध नहीं होगा सी कोड के नाम है, लेकिन मैं पूरी तरह से यकीन है कि यह कैसे लागू करने के लिए नहीं कर रहा हूँ।

इसलिए, प्रत्येक सी हेडर फ़ाइल के शीर्ष पर (गार्ड शामिल करने के बाद), हमारे पास है

#ifdef __cplusplus
extern "C" {
#endif

और सबसे नीचे, हम लिखते हैं

#ifdef __cplusplus
}
#endif

दोनों के बीच में, हमारे पास सभी प्रकार के टाइपपैड्स और फ़ंक्शन प्रोटोटाइप शामिल हैं। मेरे पास कुछ प्रश्न हैं, यह देखने के लिए कि क्या मैं इसे सही ढंग से समझ रहा हूं:

  1. अगर मेरे पास C ++ फ़ाइल A.hh है जिसमें C हेडर फ़ाइल B शामिल है, तो एक और C हेडर फ़ाइल Ch शामिल है, यह कैसे काम करता है? मुझे लगता है कि जब कंपाइलर भ में कदम रखता __cplusplusहै, तो इसे परिभाषित किया जाएगा, इसलिए यह कोड को इस ब्लॉक के साथ लपेटेगा extern "C" (और __cplusplusइसे इस ब्लॉक के अंदर परिभाषित नहीं किया जाएगा)। इसलिए, जब यह Ch में कदम रखता है, तो इसे __cplusplusपरिभाषित नहीं किया जाएगा और कोड को लपेटा नहीं जाएगा extern "C"। क्या ये सही है?

  2. कोड का एक टुकड़ा लपेटने में कुछ गड़बड़ है extern "C" { extern "C" { .. } }? दूसरा क्या करेगा extern "C" ?

  3. हम .c फ़ाइलों के चारों ओर इस आवरण को डालते हैं, केवल .h फ़ाइलों के लिए। तो, क्या होता है अगर किसी फ़ंक्शन का प्रोटोटाइप नहीं है? क्या कंपाइलर को लगता है कि यह C ++ फ़ंक्शन है?

  4. हम कुछ तृतीय-पक्ष कोड का भी उपयोग कर रहे हैं, जो C में लिखा गया है , और इसके चारों ओर इस प्रकार का आवरण नहीं है। किसी भी समय मैं उस लाइब्रेरी से एक हेडर शामिल करता हूं, मैं extern "C"लगभग #include डाल रहा हूं । क्या इससे निपटने का सही तरीका है?

  5. अंत में, क्या यह एक अच्छा विचार है? क्या हमें कुछ और करना चाहिए? हम अग्रगण्य भविष्य के लिए C और C ++ को मिलाते जा रहे हैं, और मैं यह सुनिश्चित करना चाहता हूं कि हम अपने सभी आधारों को कवर कर रहे हैं।



2
संक्षेप में, यह सबसे अच्छा स्पष्टीकरण है: To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed. (मुझे यह लिंक से मिला है )
anhldbk

आपको सी भाषा का नाम बोल्ड करने की आवश्यकता नहीं है
एडवर्ड करक

जवाबों:


290

extern "C"वास्तव में कंपाइलर कोड को पढ़ने के तरीके को नहीं बदलता है। यदि आपका कोड एक .c फ़ाइल में है, तो इसे C के रूप में संकलित किया जाएगा, यदि यह .cpp फ़ाइल में है, तो इसे C ++ के रूप में संकलित किया जाएगा (जब तक कि आप अपने कॉन्फ़िगरेशन के लिए कुछ अजीब नहीं करते)।

क्या extern "C"लिंकिंग प्रभावित करती है। C ++ फ़ंक्शंस, जब संकलित किया जाता है, तो उनके नाम मंगाए गए हैं - यह वही है जो ओवरलोडिंग को संभव बनाता है। प्रकार और मापदंडों की संख्या के आधार पर फ़ंक्शन नाम को संशोधित किया जाता है, ताकि एक ही नाम वाले दो कार्यों में अलग-अलग प्रतीक नाम होंगे।

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

extern "C"घोंसले को अच्छी तरह से रोकता है। वहाँ भी extern "C++"अगर आप अपने आप को निराशा के अंदर फंस पाते हैंextern "C" क्षेत्रों के हैं, लेकिन यह स्वच्छता के दृष्टिकोण से इतना अच्छा विचार नहीं है।

अब, विशेष रूप से आपके गिने हुए प्रश्नों के बारे में:

# 1 के बारे में: __cplusplus extern "C"ब्लॉकों के अंदर परिभाषित रहेगा । हालांकि, इससे कोई फर्क नहीं पड़ता, क्योंकि ब्लॉक को बड़े करीने से घोंसला बनाना चाहिए।

# 2 के बारे में: __cplusplus C ++ कंपाइलर के माध्यम से चलने वाली किसी भी संकलन इकाई के लिए परिभाषित किया जाएगा। आम तौर पर, इसका मतलब है कि .cpp फ़ाइलें और उस .cpp फ़ाइल द्वारा शामिल की जा रही कोई भी फ़ाइल। समान .h (या .hh या .hpp या what-have-you) की व्याख्या अलग-अलग समय पर C या C ++ के रूप में की जा सकती है, यदि विभिन्न संकलन इकाइयों में वे शामिल हैं। यदि आप चाहते हैं कि। C फ़ाइल में प्रोटोटाइप को C प्रतीक नामों से संदर्भित किया जाए, तो उन्हें extern "C"C ++ के रूप में व्याख्या किए जाने पर होना चाहिए, और C के रूप में व्याख्या किए जाने पर उनके पास नहीं होना चाहिए extern "C"- इसलिए #ifdef __cplusplusजाँच।

आपके प्रश्न का उत्तर देने के लिए # 3: प्रोटोटाइप के बिना कार्यों में C ++ लिंकेज होगा यदि वे .cpp फ़ाइलों में हैं और extern "C"ब्लॉक के अंदर नहीं हैं । हालांकि, यह ठीक है, क्योंकि यदि इसका कोई प्रोटोटाइप नहीं है, तो इसे केवल उसी फ़ाइल में अन्य फ़ंक्शन द्वारा कॉल किया जा सकता है, और फिर आप आमतौर पर परवाह नहीं करते हैं कि लिंकेज कैसा दिखता है, क्योंकि आप उस फ़ंक्शन के होने की योजना नहीं बना रहे हैं वैसे भी एक ही संकलन इकाई के बाहर कुछ भी कहा जाता है।

# 4 के लिए, आपको यह बिल्कुल मिल गया है। यदि आप कोड के लिए एक हेडर शामिल कर रहे हैं जिसमें सी लिंकेज है (जैसे कि कोड जिसे सी कंपाइलर द्वारा संकलित किया गया था), तो आपको extern "C"हेडर चाहिए - इस तरह से आप लाइब्रेरी के साथ लिंक कर पाएंगे। (अन्यथा, आपका लिंकर ऐसे नामों की तलाश में होगा जैसे _Z1hicआप कब देख रहे थेvoid h(int, char)

5: इस तरह का मिश्रण उपयोग करने का एक सामान्य कारण है extern "C", और मुझे इसे इस तरह से करने में कुछ भी गलत नहीं दिखता है - बस सुनिश्चित करें कि आप समझ रहे हैं कि आप क्या कर रहे हैं।


10
उल्लेख के लिए अच्छा है extern "C++"जब आपका C ++ हैडर / कोड कुछ C कोड के अंदर गहरे फंसा हुआ है
deddebme

1
मैंने एक साधारण सी प्रोग्राम लिखा। इसके अंदर मैंने #ifdef __cplusplus ब्लॉक जोड़ा और एक printf ("__ cplusplus परिभाषित \ n") जोड़ा; इस में। यदि मैं इसे gcc के साथ संकलित करता हूं, तो "__cplusplus परिभाषित" मुद्रित नहीं होता है, लेकिन अगर मैं इसे g ++ के साथ संकलित करता हूं, तो यह मुद्रित होता है। इसलिए मुझे लगता है कि __cplusplus का मतलब है कि कंपाइलर C ++ कंपाइलर है (आपने कहा था)। क्या यह सही नहीं है? (क्योंकि मैंने आपको यह कहते हुए देखा था कि ccplusplus को "C" ब्लॉक के अंदर परिभाषित किया जाना चाहिए। क्या हम __cplusplus को स्पष्ट रूप से परिभाषित कर सकते हैं?
Chan Kim

1
जबकि आपको अपनी इच्छानुसार (लगभग) कुछ भी परिभाषित करने में सक्षम होना चाहिए, यह __cplusplusनिर्धारित करने के लिए संपूर्ण बिंदु है कि क्या C++इसका उपयोग बनाम किया जा रहा है C, इसलिए इसे मैन्युअल रूप से परिभाषित करना / स्पष्ट रूप से इसका उद्देश्य
निर्धारित

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

39
  1. extern "C"__cplusplusमैक्रो की उपस्थिति या अनुपस्थिति को नहीं बदलता है । यह सिर्फ लिपटे हुए घोषणाओं के लिंकेज और नाम-परिवर्तन को बदलता है।

  2. आप extern "C"ब्लॉकों को काफी खुशी से घोंसला कर सकते हैं।

  3. यदि आप अपनी .cफ़ाइलों को C ++ के रूप में संकलित करते हैं, तो एक extern "C"ब्लॉक में कुछ भी नहीं , और बिना extern "C"प्रोटोटाइप को C ++ फ़ंक्शन के रूप में माना जाएगा। यदि आप उन्हें C के रूप में संकलित करते हैं तो निश्चित रूप से सब कुछ C फ़ंक्शन होगा।

  4. हाँ

  5. आप इस तरह से सी और सी ++ को सुरक्षित रूप से मिला सकते हैं।


यदि आप .cफ़ाइलों को C ++ के रूप में संकलित करते हैं, तो सब कुछ C ++ कोड के रूप में संकलित किया जाता है, भले ही वह किसी extern "C"ब्लॉक में हो। extern "C"कोड सुविधाओं है कि सी ++ पर फोन सम्मेलनों (जैसे ऑपरेटर ओवरलोडिंग), लेकिन समारोह के शरीर निर्भर अभी भी सी के रूप में संकलित किया गया है ++ उपयोग नहीं कर सकते, वह सब जरूरत पर जोर देता है।
डेविड सी।

21

एंड्रयू शेलानस्की के उत्कृष्ट उत्तर के लिए सहकर्मी हैं और थोड़े से असहमत होने के लिए कोडेचा के एक जोड़े ने वास्तव में उस तरह से बदलाव नहीं किया है जैसे कि कंपाइलर कोड पढ़ता है

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

कंपाइलर त्रुटियां होंगी यदि आप प्रोटोटाइप घोषणा की सी ++ सुविधाओं जैसे कि ओवरलोडिंग का उपयोग करने का प्रयास करते हैं।

लिंकर की त्रुटियां बाद में होंगी क्योंकि आपका फ़ंक्शन नहीं पाया जाएगा, यदि आपके पास घोषणाओं के आसपास बाहरी "सी" आवरण नहीं है और हेडर सी और सी ++ स्रोत के मिश्रण में शामिल है।

संकलन सी का उपयोग करके लोगों को हतोत्साहित करने का एक कारण C ++ सेटिंग है क्योंकि इसका मतलब है कि उनका स्रोत कोड अब पोर्टेबल नहीं है। वह सेटिंग एक प्रोजेक्ट सेटिंग है और इसलिए यदि किसी .c फ़ाइल को किसी अन्य प्रोजेक्ट में छोड़ दिया जाता है, तो उसे c ++ के रूप में संकलित नहीं किया जाएगा। मैं बल्कि लोगों को .cpp करने के लिए फ़ाइल प्रत्यय का नाम बदलने के लिए समय लेना चाहिए।


1
यह मेरे बालों को बाहर खींचने का गुप्त कारण था। वास्तव में कहीं पोस्ट करने की जरूरत है।
मिशेल करी

3

यह एबीआई के बारे में है, सी और सी ++ आवेदन दोनों को बिना किसी मुद्दे के सी इंटरफेस का उपयोग करने के लिए।

चूंकि सी भाषा बहुत आसान है, विभिन्न कंपाइलरों के लिए कोड पीढ़ी कई वर्षों तक स्थिर थी, जैसे कि जीसीसी, बोरलैंड C \ C ++, MSVC आदि।

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

लोग अभी भी C ++ में वास्तविक कार्यक्रम को लागू करना पसंद कर सकते हैं, लेकिन फिर भी पुराने C इंटरफ़ेस और ABI को हमेशा की तरह बनाए रखेंगे, हेडर फ़ाइल को बाहरी "C" {} घोषित करना होगा , यह कम्पाइलर को संगत / पुराना / सरल / आसान C ABI बताता है। यदि कंपाइलर C कंपाइलर है C ++ कंपाइलर नहीं है तो इंटरफेस फंक्शन्स के लिए

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.