C / C ++ मुख्य argv को "char * argv" के बजाय "char * argv []" क्यों घोषित किया गया है?


21

argvकेवल "सरणी के पहले सूचकांक के लिए एक सूचक" ( char* argv) होने के बजाय "सरणी के पहले सूचकांक को सूचक" के रूप में क्यों घोषित किया जाता है ?

यहाँ "पॉइंटर टू पॉइंटर" की धारणा की आवश्यकता क्यों है?


4
"एरे के पहले इंडेक्स को पॉइंटर को पॉइंटर" - यह सही विवरण नहीं है char* argv[]या char**। यह एक चरित्र के लिए एक सूचक का सूचक है; विशेष रूप से बाहरी पॉइंटर एक सरणी में पहले पॉइंटर को इंगित करता है, और इनर पॉइंटर न्यूल-टर्म स्ट्रिंग्स के पहले पात्रों को इंगित करता है। यहां कोई सूचकांक शामिल नहीं है।
सेबेस्टियन रेडल

12
अगर यह सिर्फ चार * argv होता तो आपको दूसरा तर्क कैसे मिलता?
gnasher729

15
जब आप सही जगह पर जगह डालेंगे तो आपका जीवन आसान हो जाएगा। char* argv[]जगह को गलत जगह लगा देता है। कहो char *argv[], और अब यह स्पष्ट है कि इसका अर्थ है "अभिव्यक्ति *argv[n]प्रकार का एक चर है char"। एक पॉइंटर क्या है और एक पॉइंटर को पॉइंटर क्या है, और इसी तरह से काम करने की कोशिश में मत फंसो। घोषणापत्र आपको बता रहा है कि इस चीज पर आप कौन से ऑपरेशन कर सकते हैं।
एरिक लिपर्ट

1
मानसिक रूप char * argv[]से समान सी ++ निर्माण की तुलना करें std::string argv[], और पार्स करना आसान हो सकता है। ... बस इसे इस तरह से लिखना शुरू न करें !
जस्टिन टाइम -

2
@EricLippert ध्यान दें कि प्रश्न में C ++ भी शामिल है, और वहां आप उदाहरण के char &func(int);लिए &func(5)टाइप नहीं कर सकते हैं char
रुस्लान

जवाबों:


59

आर्गव मूल रूप से इस तरह है:

यहां छवि विवरण दर्ज करें

बाईं ओर तर्क ही है - मुख्य रूप से तर्क के रूप में वास्तव में क्या पारित किया गया है। इसमें पॉइंटर्स की एक सरणी का पता होता है। उनमें से प्रत्येक स्मृति में कमांड लाइन पर पारित किए गए संगत तर्क के पाठ वाले किसी स्थान को इंगित करता है। फिर, उस सरणी के अंत में एक शून्य सूचक होने की गारंटी है।

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


52
जो भी लेआउट इंजन आकर्षित किया है कि आपके लिए आरेख उनके न्यूनतम-क्रॉसिंग एल्गोरिथ्म में एक बग है!
एरिक लिपर्ट

43
@EricLippert इस बात पर जोर देने के लिए जानबूझकर किया जा सकता है कि पॉइंटर न तो सन्निहित हो सकता है और न ही क्रम में।
२०:५० बजे

3
मैं कहूंगा कि यह जानबूझकर है
माइकल

24
यह निश्चित रूप से जानबूझकर किया गया था - मुझे लगता है कि एरिक शायद यह समझ गया था, लेकिन (सही ढंग से, आईएमओ) ने सोचा कि टिप्पणी वैसे भी हास्यास्पद थी।
जेरी कॉफिन

2
@ जेरेकॉफिन, एक यह भी इंगित कर सकता है कि भले ही वास्तविक तर्क स्मृति में सन्निहित थे, उनकी मनमानी लंबाई हो सकती है, इसलिए argv[i]सभी को पिछले सभी के माध्यम से स्कैन किए बिना एक्सेस करने में सक्षम होने के लिए अलग-अलग बिंदुओं की आवश्यकता होगी ।
इलकाचू

22

क्योंकि यही ऑपरेटिंग सिस्टम :-) प्रदान करता है

आपका प्रश्न चिकन / अंडे के उलटा होने का एक छोटा सा मुद्दा है। समस्या यह नहीं चुनना है कि आप C ++ में क्या चाहते हैं, समस्या यह है कि आप C ++ में यह कैसे कहें कि OS आपको क्या दे रहा है।

यूनिक्स "स्ट्रिंग्स" की एक सरणी से गुजरता है, प्रत्येक स्ट्रिंग एक कमांड तर्क है। सी / सी ++ में, एक स्ट्रिंग एक "चार *" है, इसलिए स्ट्रिंग्स का एक सरणी स्वाद के अनुसार चार * आरजीवी [], या चार ** आरजीवी है।


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

14
@Joker_vD मैं एक मुड़ जिस तरह से यह लगता है कि में है क्या ओएस आप देता है के बारे में। विशेष रूप से: मुझे लगता है कि C ++ ने इसे इस तरह से किया क्योंकि C ने इस तरह से किया, और C ने इस तरह से किया क्योंकि उस समय C और Unix इतने अटूट थे और Unix ने इसे इस तरह से किया था।
डैनियल वैगनर

1
@ डैनियलवागनर: हां, यह सी की यूनिक्स विरासत से है। यूनिक्स / लिनक्स पर एक न्यूनतम _startजिसे कॉल mainकरने के लिए स्मृति में mainमौजूदा argvसरणी के लिए एक सूचक पास करने की आवश्यकता होती है; यह पहले से ही सही प्रारूप में है। कर्नेल इसे argv तर्क से execve(const char *filename, char *const argv[], char *const envp[])सिस्टम कॉल पर कॉपी करता है जो एक नया निष्पादन योग्य शुरू करने के लिए बनाया गया था। (लिनक्स पर, argv [] (स्वयं सरणी) और argc प्रक्रिया प्रविष्टि पर स्टैक पर हैं। मुझे लगता है कि अधिकांश यूनिक्स समान हैं, क्योंकि इसके लिए एक अच्छी जगह है।)
पीटर कॉर्ड्स

8
लेकिन यहां जोकर का कहना यह है कि C / C ++ मानकों ने इसे लागू करने के लिए छोड़ दिया है जहां से आर्गन आते हैं; उन्हें सीधे OS से नहीं होना चाहिए। एक फ्लैट स्ट्रिंग पास करने वाले ओएस पर, एक अच्छा C ++ कार्यान्वयन argc=2में पूरे फ्लैट स्ट्रिंग को स्थापित करने और पारित करने के बजाय, टोकन को शामिल करना चाहिए । (मानक के अक्षर का पालन करना उपयोगी होने के लिए पर्याप्त नहीं है ; यह जानबूझकर कार्यान्वयन विकल्पों के लिए बहुत जगह छोड़ देता है।) हालांकि कुछ विंडोज प्रोग्राम विशेष रूप से उद्धरण का इलाज करना चाहेंगे, इसलिए वास्तविक कार्यान्वयन फ्लैट स्ट्रिंग प्राप्त करने का एक तरीका प्रदान करते हैं, भी।
पीटर कॉर्ड्स

1
बासेल का जवाब बहुत ज्यादा है + @ जोकर के सुधार और मेरी टिप्पणी, अधिक विवरण के साथ।
पीटर कॉर्डेस

15

सबसे पहले, एक पैरामीटर घोषणा के रूप में, के रूप char **argvमें ही है char *argv[]; वे दोनों तार के लिए एक सूचक (एक सरणी या एक या अधिक संभव) सूचक (ओं) का संकेत देते हैं।

अगला, अगर आपके पास केवल "पॉइंटर टू चार" है - उदाहरण के लिए char *- तो nth आइटम एक्सेस करने के लिए, आपको nth आइटम की शुरुआत का पता लगाने के लिए पहले n-1 आइटम को स्कैन करना होगा। (और यह भी आवश्यकता है कि प्रत्येक तार संचित रूप से संचित होता है।)

संकेत के सरणी के साथ, आप सीधे nth आइटम को इंडेक्स कर सकते हैं - इसलिए (कड़ाई से आवश्यक नहीं है - यह मानते हुए कि तार सन्निहित हैं) यह आमतौर पर बहुत अधिक सुविधाजनक है।

चित्रित करना:

./program हैलो दुनिया

argc = 3
argv[0] --> "./program\0"
argv[1] --> "hello\0"
argv[2] --> "world\0"

यह संभव है कि, पात्रों के एक ओएस प्रदान की गई सरणी में:

            "./program\0hello\0world\0"
argv[0]      ^
argv[1]                 ^
argv[2]                        ^

अगर argv सिर्फ एक "charter to char" थे, तो आप देख सकते हैं

       "./program\0hello\0world\0"
argv    ^

हालांकि (हालांकि ओएस के डिजाइन द्वारा संभावना है) कोई वास्तविक गारंटी नहीं है कि तीन तार "./program", "हैलो", और "दुनिया" सन्निहित हैं। इसके अलावा, इस तरह का "सिंगल पॉइंटर टू मल्टिपल कॉन्टेजिंग स्ट्रिंग्स" एक अधिक असामान्य डेटा प्रकार का निर्माण है (सी के लिए), विशेष रूप से स्ट्रिंग के लिए पॉइंटर्स के साथ तुलना में।


क्या हुआ अगर इसके बजाय, argv --> "hello\0world\0"आपके पास argv --> index 0 of the array(हैलो), एक सामान्य सरणी की तरह है। ऐसा करने योग्य क्यों नहीं है? तो आप सरणी argcसमय पढ़ते रहते हैं । तब आप argv को पास करते हैं और argv को पॉइंटर नहीं।
एक उपयोगकर्ता

@auser, कि क्या argv -> "./program\0hello\0\world\0" है: पहला पॉइंटर का सूचक (यानी ""।) यदि आप उस पॉइंटर को पहले \ 0 पर ले जाते हैं, तो आप "hello \ 0" के लिए एक सूचक है, और उसके बाद "world \ 0" के लिए। Argc बार (\ 0 "मारकर) के बाद, आप कर रहे हैं। निश्चित रूप से, यह काम करने के लिए बनाया जा सकता है, और जैसा कि मैंने कहा, एक असामान्य निर्माण।
Erik Eidt

आप यह argv[4]NULL
बताना

3
एक गारंटी है कि (कम से कम शुरू में) argv[argc] == NULL। इस मामले में ऐसा argv[3]नहीं है argv[4]
मिरल

1
@ हाँ, हाँ, धन्यवाद के रूप में मैं अशक्त चरित्र टर्मिनलों के बारे में स्पष्ट होने की कोशिश कर रहा था (और उस एक को याद किया)।
एरिक इद्दत

13

C / C ++ मुख्य argv को "char * argv []" क्यों घोषित किया जाता है

एक संभावित उत्तर इसलिए है क्योंकि C11 मानक n1570 ( .15.1.2.2.1 प्रोग्राम स्टार्टअप में ) और C ++ 11 मानक n3337 ( §3.6.1 मुख्य कार्य में ) को होस्ट किए गए वातावरण के लिए आवश्यक है (लेकिन ध्यान दें कि C मानक उल्लेख करता है भी §5.1.2.1 फ्रीस्टैंडिंग वातावरण भी) देखें इस

अगला सवाल यह है कि C और C ++ मानकों mainने ऐसा int main(int argc, char**argv)हस्ताक्षर क्यों चुना? स्पष्टीकरण काफी हद तक ऐतिहासिक है: सी का आविष्कार यूनिक्स के साथ किया गया था , जिसमें एक शेल होता है जो करने से पहले ग्लोबिंग करता है fork(जो एक प्रक्रिया बनाने के लिए एक सिस्टम कॉल है) और execve(जो एक प्रोग्राम को निष्पादित करने के लिए सिस्टम कॉल है), और वह execveएक सरणी प्रसारित करता है स्ट्रिंग कार्यक्रम के तर्क और mainनिष्पादित कार्यक्रम से संबंधित है । यूनिक्स दर्शन के बारे में और एबीआई के बारे में अधिक पढ़ें ।

और सी ++ ने सी के सम्मेलनों का पालन करने और इसके साथ संगत होने के लिए कड़ी मेहनत की। यह mainसी परंपराओं के साथ असंगत होने को परिभाषित नहीं कर सकता है।

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

व्यवहार में, mainकुछ crt0 कोड द्वारा लागू किया जाता है । ध्यान दें कि विंडोज पर ग्लोबिंग प्रत्येक प्रोग्राम द्वारा crt0 के बराबर किया जा सकता है, और कुछ विंडोज प्रोग्राम गैर-मानक WinMain प्रविष्टि बिंदु के माध्यम से शुरू कर सकते हैं । यूनिक्स पर, ग्लोबिंग शेल द्वारा किया जाता है (और crt0एबीआई को गोद ले रहा है, और प्रारंभिक कॉल स्टैक लेआउट जिसे उसने निर्दिष्ट किया है, आपके सी कार्यान्वयन के सम्मेलनों को कॉल करने के लिए)।


12

इसे "पॉइंटर टू पॉइंटर" के रूप में सोचने के बजाय, इसे "अरेंजिंग ऑफ स्ट्रिंग्स" के रूप में सोचने में मदद करता है, जिसमें []ऐरे को char*दर्शाते हैं और स्ट्रिंग को दर्शाते हैं। जब आप कोई प्रोग्राम चलाते हैं, तो आप इसे एक या एक से अधिक कमांड-लाइन तर्क पास कर सकते हैं और ये तर्कों में परिलक्षित होते हैं main: argcयह तर्कों की गिनती है और argvआपको व्यक्तिगत तर्कों का उपयोग करने देता है।


2
+1 यह! कई भाषाओं में - bash, PHP, C, C ++ - argv स्ट्रिंग्स की एक सरणी है। इसमें से आपको सोचना है कि आप कब देखते हैं char **या char *[], जो समान है।
रेक्सकोगिटंस

1

कई मामलों में जवाब "क्योंकि यह एक मानक है"। C99 मानक उद्धृत करने के लिए :

- यदि argc का मान शून्य से अधिक है, तो argv [argc-1] के माध्यम से सरणी सदस्य argv [argc-1] को सम्मिलित करते हैं , जिसमें प्रोग्राम स्टार्टअप से पहले होस्ट परिवेश द्वारा कार्यान्वयन-परिभाषित मान दिए जाते हैं।

बेशक, इसे मानकीकृत करने से पहले यह कमांड लाइन मापदंडों (कुछ चीजें जो आपको यूनिक्स शेल में देखभाल करनी है /bin/bashया जैसे /bin/shएम्बेडेड सिस्टम में नहीं हैं) के उद्देश्य से प्रारंभिक यूनिक्स कार्यान्वयन में केएंडआर सी द्वारा पहले से ही उपयोग में थी । च के शब्दों में कश्मीर एंड आर के "सी प्रोग्रामिंग भाषा" (। स्नातकोत्तर 110) के irst संस्करण :

पहला (पारंपरिक रूप से argc कहा जाता है ) कमांड-लाइन तर्कों की संख्या है जिसे कार्यक्रम के साथ लागू किया गया था; दूसरा ( argv ) वर्ण स्ट्रिंग के एक सूचक के लिए एक संकेतक है जिसमें तर्क होते हैं, एक स्ट्रिंग।

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