C में एक्सटर्नल कीवर्ड का सही उपयोग कैसे करें


235

मेरा प्रश्न यह है कि जब externC में किसी फ़ंक्शन को कीवर्ड के साथ संदर्भित किया जाना चाहिए ।

मैं यह देखने में विफल हो रहा हूं कि इसका उपयोग कब किया जाना चाहिए। जैसा कि मैं एक प्रोग्राम लिख रहा हूं जो मेरे द्वारा उपयोग किए जाने वाले सभी कार्यों को उन हेडर फ़ाइलों के माध्यम से उपलब्ध कराया गया है जिन्हें मैंने शामिल किया है। तो externहेडर फ़ाइल में उजागर होने वाली किसी चीज़ तक पहुंच प्राप्त करना उपयोगी क्यों होगा ?

मैं सोच सकता हूं कि कैसे externगलत तरीके से काम करता है, और यदि ऐसा है तो कृपया मुझे सुधारें।

संपादित करें: क्या आपको externहेडर फ़ाइल में कीवर्ड के बिना डिफ़ॉल्ट घोषणा के लिए कुछ करना चाहिए ?


कार्यों के लिए संबंधित: stackoverflow.com/questions/856636/... varables के लिए: stackoverflow.com/questions/1433204
सिरो Santilli郝海东冠状病六四事件法轮功

जवाबों:


290

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

फ़ंक्शंस और वैरिएबल्स पर "एक्सटर्नल" के बीच एक अंतर है: वैरिएबल पर यह वैरिएबल को स्वयं ही नहीं करता है, अर्थात कोई मेमोरी आवंटित नहीं करता है। इसे कहीं और करने की जरूरत है। इस प्रकार यह महत्वपूर्ण है यदि आप चर को कहीं और से आयात करना चाहते हैं। कार्यों के लिए, यह केवल संकलक को बताता है कि लिंकेज बाहरी है। जैसा कि यह डिफ़ॉल्ट है (आप "स्थिर" कीवर्ड का उपयोग करते हैं यह इंगित करने के लिए कि एक फ़ंक्शन बाहरी लिंकेज का उपयोग करने के लिए बाध्य नहीं है) आपको इसे स्पष्ट रूप से उपयोग करने की आवश्यकता नहीं है।


1
फिर वही बाहरी चीज Git में क्यों है: एक बहुत ही लोकप्रिय और आधुनिक सॉफ्टवेयर इसे जांचें: github.com/git/git/blob/master/strbuf.h
rsjethani

K & R ध्यान दें कि यह फ़ंक्शन को "बाहरी" घोषित करने के लिए डिफ़ॉल्ट है, हालांकि यह उत्तर मेरी उलझन को हल करता है!
अक्जेंट्री

@rsjethani मुझे लगता है कि दस्तावेज़ को और अधिक सख्त और प्रारूप बनाना है।
अक्जेंट्री

शायद एक गूंगा सवाल, लेकिन यह आगे की घोषणा से कैसे तुलना करता है?
वेबर 2

196

एक्सटर कंपाइलर को बताता है कि यह डेटा कहीं परिभाषित है और लिंकर से जुड़ा होगा।

यहां प्रतिक्रियाओं की मदद से और कुछ दोस्तों के साथ यहां बात करना एक्सटर्नल के उपयोग का व्यावहारिक उदाहरण है ।

उदाहरण 1 - गड्ढा दिखाने के लिए:

File stdio.h:

int errno;
/* other stuff...*/

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

MyCFile1.o और myCFile2.o से जुड़े हुए हैं, तो ग फ़ाइलों में से प्रत्येक की अलग प्रतियां errno । यह एक समस्या है क्योंकि सभी लिंक की गई फ़ाइलों में समान इरिनो उपलब्ध होना चाहिए।

उदाहरण 2 - फिक्स।

File stdio.h:

extern int errno;
/* other stuff...*/

File stdio.c

int errno;

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

अब दोनों myCFile1.o और MyCFile2.o लिंकर से वे एक ही करने के लिए दोनों बिंदु से जुड़े हुए हैं, तो errno । इस प्रकार, के साथ कार्यान्वयन को सुलझाने निर्वासन


70
समस्या यह नहीं है कि myCFile1 और myCFile2 मॉड्यूल में अरिनो की एक अलग प्रति है, यह है कि वे दोनों "अर्नो" नामक एक प्रतीक को उजागर कर रहे हैं। जब लिंकर इसे देखता है, तो यह नहीं पता होता है कि कौन से "ग़लती" को चुनना है, इसलिए यह एक त्रुटि संदेश के साथ जमानत करेगा।
16

2
"लिंकर द्वारा जुड़ा हुआ" वास्तव में क्या मतलब है? हर कोई इस शब्द का उपयोग करता है, मुझे कोई परिभाषा नहीं मिलती है :(
मार्सेल फॉलियर

7
@MarcelFalliere विकी ~ कंपाइलर प्रत्येक स्रोत फ़ाइल को अपने आप संकलित करता है और प्रत्येक स्रोत फ़ाइल के लिए एक ऑब्जेक्ट फ़ाइल बनाता है। लिंकर इन ऑब्जेक्ट फ़ाइलों को 1 निष्पादन योग्य से जोड़ता है।
बिटरबेल्यू

1
@ सीवीसी जीसीसी उपयोग करने के बाद भी एक त्रुटि या चेतावनी नहीं दे रहा है -Wallऔर -pedantic। क्यों ? और कैसे ?
b-ak

6
क्या इस सटीक चीज़ से रक्षा करने वाला गार्ड शामिल नहीं है?
obskyr

32

यह पहले ही कहा जा चुका है कि externकीवर्ड कार्यों के लिए निरर्थक है।

संकलन इकाइयों में साझा किए गए चरों के लिए, आपको उन्हें बाहरी कीवर्ड के साथ हेडर फ़ाइल में घोषित करना चाहिए, फिर उन्हें एक्सटर्नल कीवर्ड के बिना एक स्रोत फ़ाइल में परिभाषित करें। सबसे अच्छा अभ्यास के लिए हेडर फ़ाइल का नाम साझा करने वाला एकल स्रोत फ़ाइल होना चाहिए।


@aib "फ़ंक्शन के लिए निरर्थक", ब्लूब्रथर के उत्तर में मेरी टिप्पणी की जाँच करें।
rsjethani

क्या होगा अगर आप हेडर फ़ाइल में किसी भी फ़ंक्शन को उजागर नहीं करना चाहते हैं? बेहतर नहीं होगा कि एक सी फाइल में वेरिएबल को घोषित किया जाए और दूसरे में इसे एक्सटर्नल द्वारा एक्सेस किया जाए; लिंक करने वाले को समस्या का समाधान करने दें और बाकी शीर्षकों को छिपाएं।
चरण 3

16

कई साल बाद, मुझे इस सवाल का पता चला। हर उत्तर और टिप्पणी को पढ़ने के बाद, मुझे लगा कि मैं कुछ विवरणों को स्पष्ट कर सकता हूं ... यह उन लोगों के लिए उपयोगी हो सकता है जो Google खोज के माध्यम से यहां आते हैं।

प्रश्न विशेष रूप से "बाहरी" कार्यों का उपयोग करने के बारे में है, इसलिए मैं वैश्विक चर के साथ "बाहरी" के उपयोग की उपेक्षा करूंगा।

आइए 3 फ़ंक्शन प्रोटोटाइप को परिभाषित करें:

//--------------------------------------
//Filename: "my_project.H"
extern int function_1(void);
static int function_2(void);
       int function_3(void);

हेडर फ़ाइल का उपयोग मुख्य स्रोत कोड द्वारा निम्नानुसार किया जा सकता है:

//--------------------------------------
//Filename: "my_project.C"
#include "my_project.H"

void main(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 1234;

संकलन और लिंक करने के लिए, हमें उसी फ़ंक्शन कोड फ़ाइल में "function_2" को परिभाषित करना चाहिए, जहां हम उस फ़ंक्शन को कॉल करते हैं। दो अन्य कार्यों को विभिन्न स्रोत कोड " .सी" में परिभाषित किया जा सकता है या वे किसी भी बाइनरी फ़ाइल ( .OBJ, * .LIB, * .DLL) में स्थित हो सकते हैं , जिसके लिए हमारे पास स्रोत कोड नहीं हो सकता है।

चलो फिर से बेहतर अंतर को समझने के लिए हेडर "my_project.H" को एक अलग "* .सी" फ़ाइल में शामिल करें। उसी परियोजना में, हम निम्नलिखित फ़ाइल जोड़ते हैं:

//--------------------------------------
//Filename: "my_big_project_splitted.C"
#include "my_project.H"

void old_main_test(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 5678;

int function_1(void) return 12;
int function_3(void) return 34;

नोटिस करने के लिए महत्वपूर्ण विशेषताएं:

  • जब किसी फ़ंक्शन को हेडर फ़ाइल में "स्थिर" के रूप में परिभाषित किया जाता है, तो कंपाइलर / लिंकर को प्रत्येक मॉड्यूल में उस नाम के साथ फ़ंक्शन का एक उदाहरण खोजना होगा जो फ़ाइल का उपयोग करता है।

  • एक फ़ंक्शन जो सी लाइब्रेरी का हिस्सा है, केवल एक मॉड्यूल को उस मॉड्यूल में केवल "स्थिर" के साथ एक प्रोटोटाइप को फिर से परिभाषित करके बदला जा सकता है। उदाहरण के लिए, मेमोरी लीक डिटेक्शन फीचर को जोड़ने के लिए किसी भी कॉल को "मॉलोक" और "फ्री" में बदलें।

  • निर्दिष्ट "बाहरी" कार्यों के लिए वास्तव में आवश्यक नहीं है। जब "स्थिर" नहीं पाया जाता है, तो एक फ़ंक्शन को हमेशा "बाहरी" माना जाता है।

  • हालांकि, "बाहरी" चर के लिए डिफ़ॉल्ट नहीं है। आम तौर पर, कोई भी हेडर फ़ाइल जो कई मॉड्यूलों में दृश्यमान होने के लिए चर को परिभाषित करती है, को "बाहरी" का उपयोग करने की आवश्यकता होती है। केवल एक अपवाद होगा यदि हेडर फ़ाइल को एक और केवल एक मॉड्यूल से शामिल करने की गारंटी दी जाती है।

    कई प्रोजेक्ट मैनेजर को तब आवश्यकता होगी कि इस तरह के वेरिएबल को मॉड्यूल की शुरुआत में रखा जाए, न कि किसी हेडर फाइल के अंदर। कुछ बड़े प्रोजेक्ट्स, जैसे कि वीडियो गेम एमुलेटर "मैम" को भी आवश्यकता होती है कि इस तरह का वैरिएबल उनके उपयोग से पहले फ़ंक्शन के ऊपर दिखाई दे।


तो क्यों वास्तव में एक स्थिर फ़ंक्शन को बाहरी लोगों की परिभाषा बनाम परिभाषा की आवश्यकता होती है? (मुझे पता है कि यह 2 साल देर से है, लेकिन यह वास्तव में समझने के लिए उपयोगी है)
SubLock69

2
यदि आप फ़ंक्शन को लाइन 100 पर कॉल करते हैं और इसे 500 लाइन पर स्थापित करते हैं, तो परिभाषा की आवश्यकता है। लाइन 100 को अपरिभाषित प्रोटोटाइप घोषित किया जाएगा। तो, आप शीर्ष के पास प्रोटोटाइप जोड़ते हैं।
क्रिश्चियन गिंग्रास

15

C में, 'बाहरी' फंक्शन प्रोटोटाइप्स के लिए निहित है, क्योंकि एक प्रोटोटाइप एक फंक्शन की घोषणा करता है जिसे कहीं और परिभाषित किया जाता है। दूसरे शब्दों में, एक फ़ंक्शन प्रोटोटाइप में डिफ़ॉल्ट रूप से बाहरी लिंकेज होता है; 'बाहरी' का उपयोग करना ठीक है, लेकिन यह निरर्थक है।

(यदि स्टेटिक लिंकेज की आवश्यकता है, तो फ़ंक्शन को अपने प्रोटोटाइप और फ़ंक्शन हेडर दोनों में 'स्थिर' घोषित किया जाना चाहिए, और ये सामान्य रूप से दोनों एक ही .c फ़ाइल में होना चाहिए)।


8

एक बहुत अच्छा लेख जो मैं externकीवर्ड के बारे में आया था , उदाहरणों के साथ: http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

हालांकि मैं इस बात से सहमत नहीं हूं कि externफ़ंक्शन घोषणाओं में उपयोग करना बेमानी है। यह एक संकलक सेटिंग माना जाता है। इसलिए मैं externफ़ंक्शन घोषणाओं में उपयोग करने की सलाह देता हूं जब इसकी आवश्यकता होती है।


3
मैंने यहाँ आने से पहले geeksforgeeks.org लेख पढ़ा है, लेकिन यह काफी खराब लिखा हुआ पाया। व्याकरणिक और वाक्यविन्यास कमियों के अलावा, यह कई बार एक ही बिंदु बनाने के लिए बहुत सारे शब्दों का उपयोग करता है और फिर महत्वपूर्ण जानकारी को स्केच करता है। उदाहरण के लिए, उदाहरण 4 में, अचानक 'somefile.h' शामिल है, लेकिन इसके अलावा इसके बारे में कुछ नहीं कहा गया है: "मान लीजिए कि somefile.h में var की परिभाषा है"। खैर, जो जानकारी हम "सपोज" कर रहे हैं वह सिर्फ उस जानकारी के लिए होती है जिसकी मुझे तलाश है। दुर्भाग्य से, इस पृष्ठ पर कोई भी उत्तर बहुत बेहतर नहीं हैं।
एलिस वैन लोईज

6

यदि आपके प्रोग्राम की प्रत्येक फाइल पहले किसी ऑब्जेक्ट फाइल में संकलित की जाती है, तो ऑब्जेक्ट फाइलें एक साथ जुड़ी हुई हैं, जिसकी आपको जरूरत है extern। यह संकलक को बताता है "यह फ़ंक्शन मौजूद है, लेकिन इसके लिए कोड कहीं और है। घबराओ मत।"


उम, यह है कि अनुवाद सामान्य रूप से कैसे किया जाता है: स्रोत फ़ाइलें ऑब्जेक्ट फ़ाइलों के लिए संकलित होती हैं, और फिर लिंक की जाती हैं। आपको उस मामले में बाहरी की आवश्यकता कब होगी? न ही आप फ़ंक्शन प्राप्त करने के लिए #include का उपयोग करेंगे, बल्कि प्रोटोटाइप कार्य करेंगे। मुझे समझ नहीं आ रहा है कि आप किस बारे में बात कर रहे हैं।
डेविड थॉर्नले

मुझे लगता है कि यह समस्या हाल ही में गलत तरीके से फैल रही है। उसके लिए माफ़ करना। जब मैं C के लिए नया था, तो मैं "file.c" को केवल एक फाइल में सीधे दूसरे फाइल में शामिल करने के लिए "file.c" को छोड़ दूंगा। तब मुझे पता चला कि 'बाहरी' का उपयोग कैसे किया जाता है। मुझे लगा कि वह वही गलती कर रहा है जो मैं कर रहा था।
क्रिस लुत्ज़

4

हेडर फ़ाइलों में फ़ंक्शन और चर की सभी घोषणाएं होनी चाहिए extern

इस नियम के अपवाद हेडर और वेरिएबल्स में परिभाषित इनलाइन फ़ंक्शन हैं - हालांकि हेडर में परिभाषित - अनुवाद इकाई के लिए स्थानीय होना होगा (हेडर में स्रोत फ़ाइल शामिल हो जाती है): ये होनी चाहिए static

स्रोत फ़ाइलों externमें, फ़ाइल में परिभाषित फ़ंक्शंस और चर के लिए उपयोग नहीं किया जाना चाहिए। केवल स्थानीय परिभाषाओं के साथ उपसर्ग करें staticऔर साझा परिभाषाओं के लिए कुछ भी न करें - वे डिफ़ॉल्ट रूप से बाहरी प्रतीक होंगे।

externस्रोत फ़ाइल में सभी का उपयोग करने का एकमात्र कारण फ़ंक्शन और चर घोषित करना है जो अन्य स्रोत फ़ाइलों में परिभाषित किए गए हैं और जिनके लिए कोई हेडर फ़ाइल प्रदान नहीं की गई है।


फंक्शन प्रोटोटाइप घोषित करना externवास्तव में अनावश्यक है। कुछ लोग इसे नापसंद करते हैं क्योंकि यह सिर्फ अंतरिक्ष को बर्बाद करेगा और फ़ंक्शन घोषणाओं में पहले से ही लाइन की सीमा को ओवरफ़्लो करने की प्रवृत्ति है। दूसरों को यह पसंद है क्योंकि इस तरह से, कार्यों और चर को उसी तरह से व्यवहार किया जा सकता है।


क्या आप एक कारण दे सकते हैं कि "हेडर फ़ाइलों में फ़ंक्शन और चर की सभी घोषणाएं बाहरी क्यों होनी चाहिए।" यह अन्य प्रतिक्रियाओं से मुझे लगता है कि वे डिफ़ॉल्ट रूप से बाहरी हैं।
lillq

@ लेन: externफ़ंक्शन घोषणाओं के लिए वैकल्पिक है, लेकिन मुझे चर और कार्यों का एक ही तरह से व्यवहार करना पसंद है - कम से कम यह सबसे उचित चीज है जिसके साथ मैं आ सकता हूं, क्योंकि मुझे याद नहीं है कि मैंने ऐसा क्यों करना शुरू किया था;)
क्रिस्टोफ़

क्या यह एक बेहतर विचार नहीं है कि हमेशा वैश्विक चर को C फ़ाइल में शामिल किया जाए, ताकि वे अन्य यादृच्छिक C फ़ाइलों द्वारा दिखाई न दें जिनमें हेडर शामिल हैं। और हमेशा स्पष्टता के मामले में प्रारंभिक सच सिंक को छोड़कर हर वैश्विक पर बाहरी उपयोग करना; यदि यह पूर्वनिर्मित बाह्य है तो इसे कहीं और परिभाषित किया गया है।
चरण 3

3

अन्य स्रोत फ़ाइलों में वास्तव में परिभाषित कार्य केवल हेडर में घोषित किए जाने चाहिए । इस स्थिति में, आपको हेडर में प्रोटोटाइप की घोषणा करते समय बाहरी उपयोग करना चाहिए ।

अधिकांश समय, आपके कार्य निम्नलिखित में से एक होंगे (सबसे अच्छा अभ्यास की तरह):

  • स्थिर (सामान्य कार्य जो उस .c फ़ाइल के बाहर दिखाई नहीं देते हैं)
  • स्थिर इनलाइन (.c या .h फ़ाइलों से इनलाइन)
  • बाहरी (अगली तरह के हेडर में घोषणा) (नीचे देखें)
  • [कोई भी कीवर्ड नहीं] (सामान्य कार्य बाहरी घोषणाओं का उपयोग करने के लिए अभिप्रेत है)

यदि यह डिफ़ॉल्ट है तो प्रोटोटाइप की घोषणा करते समय आप बाहरी क्यों करेंगे?
०३:३६ पर lillq

@ लेन: थोड़ा सा पक्षपाती हो सकता है, लेकिन मैंने जिस पर काम किया है, उस हरेक प्रोजेक्ट को निम्न कन्वेंशन का उपयोग किया जाता है: हेडर में, केवल बाहरी कार्यों (इसलिए बाहरी) के लिए प्रोटोटाइप की घोषणा करें। .C फ़ाइलों में, सादे प्रोटोटाइप का उपयोग विशिष्ट ऑर्डरिंग की आवश्यकता को कम करने के लिए किया जा सकता है, लेकिन उन्हें हेडर में नहीं रखा जाना चाहिए।
एडुआर्ड - गैब्रियल मुंटेनू

1

जब आपके पास वह फ़ंक्शन एक अलग dll या lib पर परिभाषित होता है, ताकि संकलक इसे खोजने के लिए लिंकर को हटा दे। विशिष्ट मामला तब होता है जब आप OS API से फ़ंक्शन कॉल कर रहे होते हैं।

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