C में मुख्य () विधि कैसे काम करती है?


96

मुझे पता है कि मुख्य विधि लिखने के लिए दो अलग-अलग हस्ताक्षर हैं -

int main()
{
   //Code
}

या कमांड लाइन तर्क को संभालने के लिए, हम इसे लिखते हैं-

int main(int argc, char * argv[])
{
   //code
}

में C++मैं जानता हूँ कि हम एक विधि को ओवरलोड सकता है, लेकिन में Cकैसे संकलक के इन दो अलग हस्ताक्षर का क्या करता है mainसमारोह?


14
ओवरलोडिंग से तात्पर्य एक ही कार्यक्रम में एक ही नाम से दो विधियाँ होना है आप कभी भी एक mainकार्यक्रम में एक ही विधि C(या, वास्तव में, बहुत ज्यादा किसी भी भाषा में इस तरह के निर्माण के साथ) कर सकते हैं।
काइल स्ट्रैंड

13
सी में विधियां नहीं हैं; इसके कार्य हैं। विधियाँ ऑब्जेक्ट-ओरिएंटेड "जेनेरिक" फ़ंक्शन का बैक एंड कार्यान्वयन हैं। कार्यक्रम कुछ ऑब्जेक्ट तर्कों के साथ एक फ़ंक्शन को कॉल करता है, और ऑब्जेक्ट सिस्टम उनके प्रकारों के आधार पर एक विधि (या शायद तरीकों का एक सेट) चुनता है। C के पास इस सामान की कोई जरूरत नहीं है जब तक कि आप इसे स्वयं अनुकरण नहीं करते हैं।
कज़

4
कार्यक्रम के प्रवेश बिंदुओं पर गहन चर्चा के लिए - विशेष रूप से नहीं main- मैं जॉन आर लेविंस की क्लासिक पुस्तक "लिंकर्स एंड लोडर्स" की सिफारिश करता हूं।
एंड्रियास स्पिंडलर

1
सी में, पहला फॉर्म है int main(void), नहीं int main()(हालांकि मैंने कभी एक कंपाइलर नहीं देखा है जो int main()फॉर्म को अस्वीकार करता है)।
कीथ थॉम्पसन

1
@harper: यह ()प्रपत्र अप्रचलित है, और यह स्पष्ट नहीं है कि यह इसके लिए भी अनुमति दी गई है main(जब तक कि कार्यान्वयन विशेष रूप से इसे अनुमति प्राप्त प्रपत्र के रूप में दस्तावेज नहीं करता है)। C मानक (5.1.2.2.1 प्रोग्राम स्टार्टअप देखें) ()फॉर्म का उल्लेख नहीं करता है , जो फॉर्म के समकक्ष नहीं है() । इस टिप्पणी के लिए विवरण बहुत लंबा है।
कीथ थॉम्पसन

जवाबों:


133

सी भाषा की कुछ विशेषताओं को हैक के रूप में शुरू किया गया जो सिर्फ काम करने के लिए हुआ।

मुख्य, साथ ही चर-लंबाई तर्क सूचियों के लिए कई हस्ताक्षर, उन विशेषताओं में से एक है।

प्रोग्रामर्स ने देखा कि वे एक फ़ंक्शन के लिए अतिरिक्त तर्क पास कर सकते हैं, और उनके दिए गए संकलक के साथ कुछ भी बुरा नहीं होता है।

यह मामला है यदि कॉलिंग कन्वेंशन ऐसे हैं:

  1. कॉलिंग फ़ंक्शन तर्कों को साफ़ करता है।
  2. बाईं ओर के तर्क स्टैक के शीर्ष, या स्टैक फ्रेम के आधार के करीब हैं, ताकि सहज तर्क पते को अमान्य न करें।

कॉलिंग सम्मेलनों का एक सेट जो इन नियमों का पालन करता है, स्टैक-आधारित पैरामीटर होता है जिससे कॉल करने वाले को तर्क मिलते हैं, और उन्हें दाएं से बाएं धक्का दिया जाता है:

 ;; pseudo-assembly-language
 ;; main(argc, argv, envp); call

 push envp  ;; rightmost argument
 push argv  ;; 
 push argc  ;; leftmost argument ends up on top of stack

 call main

 pop        ;; caller cleans up   
 pop
 pop

संकलक में जहां इस तरह के कॉलिंग कन्वेंशन का मामला है, दो प्रकार के main, या यहां तक ​​कि अतिरिक्त प्रकारों का समर्थन करने के लिए कुछ भी विशेष करने की आवश्यकता नहीं है । mainबिना किसी तर्क के एक समारोह हो सकता है, इस मामले में यह उन वस्तुओं से अनजान है जिन्हें स्टैक पर धकेल दिया गया था। यदि यह दो तर्क के एक समारोह है, तो यह पाता है argcऔर argvदो सर्वोच्च ढेर आइटम के रूप में। यदि यह एक पर्यावरण-सूचक (एक सामान्य विस्तार) के साथ एक प्लेटफ़ॉर्म-विशिष्ट तीन-तर्क संस्करण है, तो यह भी काम करेगा: यह स्टैक के शीर्ष से तीसरे तत्व के रूप में तीसरा तर्क मिलेगा।

और इसलिए एक निश्चित कॉल सभी मामलों के लिए काम करता है, जिससे एकल, निश्चित स्टार्ट-अप मॉड्यूल को कार्यक्रम से जोड़ा जा सकता है। उस मॉड्यूल को C से लिखा जा सकता है, एक फ़ंक्शन के जैसा होता है:

/* I'm adding envp to show that even a popular platform-specific variant
   can be handled. */
extern int main(int argc, char **argv, char **envp);

void __start(void)
{
  /* This is the real startup function for the executable.
     It performs a bunch of library initialization. */

  /* ... */

  /* And then: */
  exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere));
}

दूसरे शब्दों में, यह स्टार्ट मॉड्यूल हमेशा एक तीन-तर्क मुख्य, हमेशा कहता है। यदि मुख्य कोई तर्क नहीं लेता है, या केवल int, char **, यह ठीक काम करने के लिए होता है, साथ ही साथ अगर यह कोई तर्क नहीं लेता है, तो बुलावे के कारण।

यदि आप अपने कार्यक्रम में इस तरह की बात करते हैं, तो यह आईएसओ सी द्वारा गैर-व्यवहार योग्य और अपरिभाषित व्यवहार माना जाएगा: एक तरीके से किसी फ़ंक्शन को घोषित करना और कॉल करना, और इसे दूसरे में परिभाषित करना। लेकिन एक कंपाइलर के स्टार्टअप ट्रिक का पोर्टेबल होना जरूरी नहीं है; यह पोर्टेबल कार्यक्रमों के लिए नियमों द्वारा निर्देशित नहीं है।

लेकिन मान लीजिए कि कॉलिंग कन्वेंशन ऐसे हैं जो इस तरह से काम नहीं कर सकते हैं। उस मामले में, संकलक को mainविशेष रूप से इलाज करना होगा । जब यह नोटिस करता है कि यह mainफ़ंक्शन को संकलित कर रहा है, तो यह कोड उत्पन्न कर सकता है जो तीन तर्क कॉल के साथ संगत है।

यह कहना है, आप यह लिखते हैं:

int main(void)
{
   /* ... */
}

लेकिन जब कंपाइलर इसे देखता है, तो यह अनिवार्य रूप से एक कोड परिवर्तन करता है ताकि यह जो फंक्शन संकलित करता है वह इस तरह दिखाई दे:

int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore)
{
   /* ... */
}

सिवाय इसके कि नाम __argc_ignoreसचमुच में मौजूद नहीं हैं। इस तरह के नामों को आपके दायरे में नहीं लाया जाता है, और अप्रयुक्त तर्कों के बारे में कोई चेतावनी नहीं होगी। कोड परिवर्तन के कारण कंपाइलर सही लिंकेज के साथ कोड का उत्सर्जन करता है जो जानता है कि उसे तीन तर्कों को साफ करना है।

__startफ़ंक्शन को कस्टम-जनरेट करने के लिए कंपाइलर या संभवतः लिंकर के लिए एक और कार्यान्वयन रणनीति है (या इसे जो भी कहा जाता है), या कम से कम कई पूर्व-संकलित विकल्पों में से एक का चयन करें। ऑब्जेक्ट फ़ाइल में जानकारी को संग्रहीत किया जा सकता है कि किस समर्थित फॉर्म का mainउपयोग किया जा रहा है। लिंकर इस जानकारी को देख सकता है, और स्टार्ट-अप मॉड्यूल के सही संस्करण का चयन कर सकता है जिसमें एक कॉल होता है mainजो प्रोग्राम की परिभाषा के अनुकूल है। सी कार्यान्वयन में आमतौर पर केवल समर्थित रूपों की एक छोटी संख्या होती है mainइसलिए यह दृष्टिकोण संभव है।

C99 भाषा के कंपाइलरों को हमेशा mainहैक का समर्थन करने के लिए, कुछ हद तक, विशेष रूप से व्यवहार करना पड़ता है कि यदि फ़ंक्शन बिना returnबयान के समाप्त हो जाता है , तो व्यवहार ऐसा है जैसे कि return 0निष्पादित किया गया हो। यह, फिर से, एक कोड परिवर्तन द्वारा इलाज किया जा सकता है। संकलक ने नोटिस किया कि एक फ़ंक्शन को mainसंकलित किया जा रहा है। फिर यह जांचता है कि क्या शरीर का अंत संभावित रूप से उपलब्ध है। यदि हां, तो यह सम्मिलित करता हैreturn 0;


34

mainC ++ में भी कोई अतिभार नहीं है । मुख्य कार्य एक कार्यक्रम के लिए प्रवेश बिंदु है और केवल एक ही परिभाषा मौजूद होनी चाहिए।

स्टैंडर्ड सी के लिए

एक होस्ट किए गए वातावरण के लिए (यह सामान्य है), C99 मानक कहता है:

5.1.2.2.1 प्रोग्राम स्टार्टअप

कार्यक्रम को प्रोग्राम स्टार्टअप कहा जाता है main। कार्यान्वयन इस फ़ंक्शन के लिए कोई प्रोटोटाइप घोषित नहीं करता है। इसे किसी प्रकार के रिटर्न intपैरामीटर के साथ परिभाषित किया जाएगा :

int main(void) { /* ... */ }

या दो मापदंडों के साथ (यहाँ करने के लिए भेजा के रूप में argcऔर argv, हालांकि किसी भी नाम का इस्तेमाल किया जा सकता है, वे समारोह में वे घोषित किये गए हैं करने के लिए स्थानीय कर रहे हैं के रूप में):

int main(int argc, char *argv[]) { /* ... */ }

या उसके बराबर; 9) या कुछ अन्य कार्यान्वयन-परिभाषित तरीके से।

9) इस प्रकार, के intरूप में परिभाषित टाइपसेफ नाम से प्रतिस्थापित किया जा सकता है int, या इस प्रकार argvलिखा जा सकता है char **argv, और इसी तरह।

मानक C ++ के लिए:

3.6.1 मुख्य कार्य [basic.start.main]

1 एक कार्यक्रम में मुख्य नामक एक वैश्विक फ़ंक्शन शामिल होगा, जो कार्यक्रम की निर्दिष्ट शुरुआत है। [...]

2 एक कार्यान्वयन मुख्य कार्य को पूर्वनिर्धारित नहीं करेगायह फ़ंक्शन अतिभारित नहीं किया जाएगा । इसमें वापसी प्रकार का इंट इंट होगा, लेकिन अन्यथा इसका प्रकार कार्यान्वयन परिभाषित है। सभी कार्यान्वयन मुख्य की निम्नलिखित परिभाषाओं दोनों की अनुमति देंगे:

int main() { /* ... */ }

तथा

int main(int argc, char* argv[]) { /* ... */ }

C ++ मानक स्पष्ट रूप से कहता है "यह [मुख्य कार्य] एक वापसी प्रकार का प्रकार होगा int, लेकिन अन्यथा इसका प्रकार कार्यान्वयन परिभाषित है", और C मानक के समान दो हस्ताक्षर की आवश्यकता होती है।

एक होस्ट किए गए वातावरण में (एसी वातावरण जो सी पुस्तकालयों का भी समर्थन करता है) - ऑपरेटिंग सिस्टम कॉल करता है main

एक गैर-होस्ट किए गए वातावरण में (एम्बेडेड अनुप्रयोगों के लिए एक) आप हमेशा पूर्व-प्रोसेसर निर्देशों का उपयोग करके अपने कार्यक्रम के प्रवेश बिंदु (या निकास) को बदल सकते हैं।

#pragma startup [priority]
#pragma exit [priority]

जहां प्राथमिकता एक वैकल्पिक अभिन्न संख्या है।

प्राग्मा स्टार्टअप मुख्य (प्राथमिकता-वार) से पहले फंक्शन को अंजाम देता है और प्रैग्मा एग्जिट मुख्य फंक्शन के बाद फंक्शन को अंजाम देता है। यदि एक से अधिक स्टार्टअप निर्देश हैं तो प्राथमिकता तय करती है जो पहले निष्पादित होगी।


4
मुझे नहीं लगता, यह जवाब वास्तव में इस सवाल का जवाब देता है कि संकलक वास्तव में स्थिति को कैसे संभालता है। @ काज़ द्वारा दिया गया जवाब, मेरी राय में अधिक अंतर्दृष्टि देता है।
तिलमैन वोगेल

4
मुझे लगता है कि यह उत्तर इस प्रश्न से बेहतर उत्तर देता है @Kaz द्वारा। मूल प्रश्न इस धारणा के अंतर्गत है कि ऑपरेटर ओवरलोडिंग हो रहा है, और यह उत्तर इस बात को हल करता है कि कुछ ओवरलोडिंग समाधान के बजाय संकलक दो अलग-अलग हस्ताक्षर स्वीकार करता है। संकलक विवरण दिलचस्प हैं लेकिन प्रश्न का उत्तर देने के लिए आवश्यक नहीं है।
वलीद खान

1
फ्रीस्टैंडिंग वातावरण ("गैर-होस्टेड") के लिए, कुछ #pragma की तुलना में बहुत अधिक चल रहा है। हार्डवेयर से एक रीसेट बाधा है और यह वास्तव में जहां कार्यक्रम शुरू होता है। वहां से, सभी मौलिक सेटअप निष्पादित किए जाते हैं: सेटअप स्टैक, रजिस्टर, MMU, मेमोरी मैपिंग आदि। फिर NVM से स्टेटिक स्टोरेज वैरिएबल (.data सेगमेंट) में init वैल्यू की कॉपी-डाउन, साथ ही साथ "शून्य-आउट"। स्थिर भंडारण चर जिसे शून्य (.bss खंड) पर सेट किया जाना चाहिए। C ++ में, स्थिर भंडारण अवधि वाले ऑब्जेक्ट के कंस्ट्रक्टर को कहा जाता है। और एक बार जो किया जाता है, उसके बाद मुख्य कहा जाता है।
लंडिन

8

ओवरलोडिंग की कोई जरूरत नहीं है। हां, 2 संस्करण हैं, लेकिन उस समय केवल एक का उपयोग किया जा सकता है।


5

यह सी और सी ++ भाषा के अजीब विषमताओं और विशेष नियमों में से एक है।

मेरी राय में यह केवल ऐतिहासिक कारणों से मौजूद है और इसके पीछे कोई वास्तविक गंभीर तर्क नहीं है। ध्यान दें कि mainअन्य कारणों के लिए भी विशेष है (उदाहरण के लिए mainC ++ में पुनरावर्ती नहीं हो सकता है और आप इसका पता नहीं लगा सकते हैं और C99 / C ++ में आपको एक अंतिम returnविवरण को छोड़ने की अनुमति है )।

ध्यान दें कि C ++ में भी यह एक अधिभार नहीं है ... या तो एक प्रोग्राम का पहला रूप है या इसका दूसरा रूप है; यह दोनों नहीं हो सकता।


आप returnC के साथ (C99 के बाद से) कथन को छोड़ सकते हैं ।
ड्रीमलैक्स

सी में, आप कॉल कर सकते हैं main()और इसका पता ले सकते हैं ; C ++ उन सीमाओं को लागू करता है जो C नहीं करता है।
जोनाथन लेफ्लर

@JonathanLeffler: आप सही, निश्चित हैं। C99 स्पेक्स में मुख्य मुख्य के बारे में केवल एक मजेदार बात यह है कि रिटर्न वैल्यू को छोड़ने की संभावना के अलावा यह है कि जैसा कि मानक argcIIUC में लिखा गया है, जब आप रिकैस्टिंग करते समय एक नकारात्मक मान पास नहीं कर सकते हैं (5.1.2.2.1 सीमाएँ निर्दिष्ट नहीं करता है argcऔर argvकेवल प्रारंभिक कॉल पर लागू होता है main)।
6502

4

यह असामान्य mainनहीं है कि इसे एक से अधिक तरीकों से परिभाषित किया जा सकता है, यह है कि इसे केवल दो अलग-अलग तरीकों से परिभाषित किया जा सकता है।

mainएक उपयोगकर्ता-परिभाषित फ़ंक्शन है; कार्यान्वयन इसके लिए एक प्रोटोटाइप घोषित नहीं करता है।

एक ही बात fooया के लिए सच है bar, लेकिन आप उन नामों के साथ कार्यों को परिभाषित कर सकते हैं जो किसी भी तरह से आपको पसंद हैं।

अंतर यह है कि mainकार्यान्वयन द्वारा लागू किया गया है (रनटाइम वातावरण), न केवल अपने कोड द्वारा। कार्यान्वयन साधारण सी फ़ंक्शन कॉल शब्दार्थों तक सीमित नहीं है, इसलिए यह कुछ भिन्नताओं से निपट सकता है (और चाहिए) - लेकिन यह असीम रूप से कई संभावनाओं को संभालने के लिए आवश्यक नहीं है। int main(int argc, char *argv[])फार्म और कमांड लाइन तर्क के लिए अनुमति देता है int main(void)सी में या int main()C ++ केवल साधारण प्रोग्राम हैं जो प्रक्रिया कमांड लाइन तर्क की जरूरत नहीं है के लिए एक सुविधा है।

संकलक इसे कैसे संभालता है, यह कार्यान्वयन पर निर्भर करता है। अधिकांश प्रणालियों में संभवतः कॉलिंग कन्वेंशन हैं जो दो रूपों को प्रभावी रूप से संगत बनाते हैं, और किसी भी mainपैरामीटर के साथ परिभाषित किसी भी तर्क को चुपचाप अनदेखा कर दिया जाता है। यदि नहीं, तो संकलक या लिंकर के लिए mainविशेष रूप से इलाज करना मुश्किल नहीं होगा । यदि आप उत्सुक हैं कि यह आपके सिस्टम पर कैसे काम करता है , तो आप कुछ विधानसभा सूची देख सकते हैं।

और सी और सी ++ में कई चीजों की तरह, विवरण मोटे तौर पर इतिहास और भाषाओं के डिजाइनरों और उनके पूर्ववर्तियों द्वारा किए गए मनमाने निर्णयों का परिणाम है।

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


3

mainबस लिंकर जहां द्वारा निर्णय लिया एक प्रारंभिक पते के लिए एक नाम है mainडिफ़ॉल्ट नाम है। एक कार्यक्रम में सभी फ़ंक्शन नाम पते शुरू कर रहे हैं जहां फ़ंक्शन शुरू होता है।

फ़ंक्शन तर्क को स्टैक से / पर / पुश किया जाता है, ताकि फ़ंक्शन के लिए कोई तर्क निर्दिष्ट न हो, स्टैक पर / बंद किए गए कोई तर्क नहीं हैं। इस तरह से मुख्य तर्क के साथ या बिना दोनों काम कर सकते हैं।


2

खैर, एक ही फ़ंक्शन मुख्य () के दो अलग-अलग हस्ताक्षर केवल तस्वीर में आते हैं जब आप उन्हें चाहते हैं, तो मेरा मतलब है कि अगर आपके प्रोग्राम को आपके कोड के किसी भी वास्तविक प्रसंस्करण से पहले डेटा की आवश्यकता होती है, तो आप उन्हें उपयोग कर सकते हैं -

    int main(int argc, char * argv[])
    {
       //code
    }

जहाँ चर argc, पास की गई डेटा की गिनती को संग्रहीत करता है और argv पॉइंटर्स की एक सारणी है, जो कंसोल से उत्तीर्ण मानों को इंगित करता है। अन्यथा हमेशा साथ जाना अच्छा है

    int main()
    {
       //Code
    }

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


2

इससे पहले एक समान प्रश्न पूछा गया था: कोई फ़ंक्शन जिसके पास कोई पैरामीटर नहीं है (वास्तविक फ़ंक्शन परिभाषा की तुलना में) संकलन क्यों करता है?

शीर्ष क्रम के उत्तर में से एक था:

सी में func()इसका मतलब है कि आप किसी भी तर्क को पारित कर सकते हैं । यदि आप कोई तर्क नहीं चाहते हैं, तो आपको घोषित करना होगाfunc(void)

तो, मुझे लगता है कि यह कैसे mainघोषित किया जाता है (यदि आप "घोषित" शब्द को लागू कर सकते हैं main)। वास्तव में आप ऐसा कुछ लिख सकते हैं:

int main(int only_one_argument) {
    // code
}

और यह अभी भी संकलित और चलाएगा।


1
बहुत बढ़िया अवलोकन! ऐसा प्रतीत होता है कि लिंकर काफी क्षमाशील है main, क्योंकि अभी तक कोई मुद्दा नहीं है: इसके लिए और भी तर्क दिए गए हैं main! "यूनिक्स (लेकिन Posix.1 नहीं) और माइक्रोसॉफ्ट विंडोज" जोड़ते हैं char **envp(मुझे याद है कि डॉस ने अनुमति दी कि साथ ही, यह नहीं किया?), और मैक ओएस एक्स और डार्विन अभी तक एक और "मनमानी ओएस-आपूर्ति की जानकारी" चार * पॉइंटर जोड़ते हैं। विकिपीडिया
usr2564301

0

आपको इसे ओवरराइड करने की आवश्यकता नहीं है। लेकिन केवल एक ही समय में उपयोग किया जाएगा। लेकिन मुख्य समारोह के 2 अलग-अलग संस्करण हैं

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