क्या मुझे char ** argv या char * argv [] का उपयोग करना चाहिए?


125

मैं सिर्फ C सीख रहा हूं और सोच रहा था कि इनमें से कौन सी अपनी मुख्य विधि का उपयोग करूं। क्या कोई अंतर है? कौन सा अधिक आम है?


2
मैं 'char ** argv' पसंद करता हूं, और इसलिए मैं इसे अधिक बार देखता हूं, लेकिन दोनों सही हैं और मैं केवल एक घोषणा को नहीं बदलूंगा क्योंकि इसे 'char * argv [] लिखा गया था।
जोनाथन लेफ़लर

+1 क्योंकि सरणी बनाम पॉइंटर के गहरे मुद्दे अच्छी तरह से समझने के लिए महत्वपूर्ण हैं।
RBerteig

2
वास्तव में, वास्तव में अच्छा सवाल है। धन्यवाद।
फ्रैंक वी

5
Comp.lang.c के अक्सर पूछे जाने वाले प्रश्न अनुभाग पढ़ें ; यह सी सरणियों और मेरे द्वारा देखे गए बिंदुओं के बीच संबंधों का सबसे अच्छा विवरण है। दो प्रासंगिक बिंदु: 1. एक पैरामीटर घोषणा (और केवल एक पैरामीटर घोषणा के रूप में) के char **argvबराबर है । 2. सरणियाँ संकेत नहीं हैं। char *argv[]
कीथ थॉम्पसन

जवाबों:


160

जैसा कि आप अभी C सीख रहे हैं, मैं आपको आम चीज़ों के बजाय पहले सरणियों और बिंदुओं के बीच के अंतर को समझने की कोशिश करने की सलाह देता हूँ ।

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

  • पैरामीटर के रूप में कार्य
  • पैरामीटर के रूप में आता है

पैरामीटर के रूप में आता है

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

C में, इन कारणों से, सरणी-मान मौजूद नहीं हैं। यदि आप किसी सरणी का मान प्राप्त करना चाहते हैं, तो आपको इसके बजाय जो मिलता है, वह उस सरणी के पहले तत्व का सूचक है। और यहाँ वास्तव में पहले से ही समाधान निहित है। एक सरणी पैरामीटर ड्राइंग के बजाय-सामने अमान्य, एक सी संकलक होगा परिणत संबंधित पैरामीटर के प्रकार के एक सूचक माना जाता है। यह याद रखें, यह बहुत महत्वपूर्ण है। पैरामीटर एक सरणी नहीं होगा, लेकिन इसके बजाय यह संबंधित तत्व प्रकार के लिए एक संकेतक होगा।

अब, यदि आप किसी ऐरे को पास करने की कोशिश करते हैं, तो इसके बजाय जो पास किया गया है, वह एरे के पहले एलिमेंट का पॉइंटर है।

भ्रमण: पैरामीटर के रूप में कार्य

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

void f(void g(void));
void f(void (*g)(void));

ध्यान दें कि चारों ओर कोष्ठक *gकी आवश्यकता है। अन्यथा, यह किसी फ़ंक्शन को इंगित void*करने वाले के बजाय एक फ़ंक्शन लौटने के लिए एक फ़ंक्शन को निर्दिष्ट करेगा void

वापस arrays के लिए

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

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

बेशक, इसका कोई मतलब नहीं है कि इसमें कोई भी आकार डाल सकता है, और यह सिर्फ दूर फेंक दिया जाता है। उस कारण से, C99 उन संख्याओं के लिए एक नया अर्थ लेकर आया, और अन्य चीजों को कोष्ठक के बीच प्रदर्शित करने की अनुमति देता है:

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

अंतिम दो पंक्तियों का कहना है कि आप फ़ंक्शन के भीतर "argv" को बदलने में सक्षम नहीं होंगे - यह एक कॉन्स्टेंट पॉइंटर बन गया है। हालांकि कुछ ही C कंपाइलर्स उन C99 फीचर्स को सपोर्ट करते हैं। लेकिन ये विशेषताएं यह स्पष्ट करती हैं कि "सरणी" वास्तव में एक नहीं है। यह एक पॉइंटर है।

खतरे के संकेत के लिए एक शब्द ही काफी है

ध्यान दें कि ऊपर कहा गया सब तभी सत्य है जब आपको किसी फ़ंक्शन के पैरामीटर के रूप में एक सरणी मिली हो । यदि आप स्थानीय सरणियों के साथ काम करते हैं, तो एक सरणी पॉइंटर नहीं होगी। यह एक सूचक के रूप में व्यवहार करेगा , क्योंकि जैसा कि पहले बताया गया है कि एक सरणी को सूचक में परिवर्तित किया जाएगा जब इसका मूल्य पढ़ा जाता है। लेकिन यह संकेतकर्ताओं के साथ भ्रमित नहीं होना चाहिए।

एक क्लासिक उदाहरण निम्नलिखित है:

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;

2
पता नहीं था कि यह मौजूद है: * argv [स्टेटिक 1]
सुपरलुकस

12

आप या तो उपयोग कर सकते हैं। वे पूरी तरह से समकक्ष हैं। देखें लिब की टिप्पणियाँ और उसका उत्तर

यह वास्तव में निर्भर करता है कि आप इसका उपयोग कैसे करना चाहते हैं (और आप किसी भी स्थिति में उपयोग कर सकते हैं):

// echo-with-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char **argv)
{
  while (--argc > 0)
  {
    printf("%s ", *++argv);
  }
  printf("\n");
  return 0;
}

// echo-without-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  int i;
  for (i=1; i<argc; i++)
  {
    printf("%s ", argv[i]);
  }
  printf("\n");
  return 0;
}

जिसके लिए अधिक आम है - इससे कोई फर्क नहीं पड़ता। आपके कोड को पढ़ने वाला कोई भी अनुभवी सी प्रोग्रामर दोनों को विनिमेय (सही परिस्थितियों में) के रूप में देखेगा। जैसे एक अनुभवी अंग्रेजी वक्ता "वे" और "वे" समान रूप से आसानी से पढ़ते हैं।

अधिक महत्वपूर्ण यह है कि आप उन्हें पढ़ना सीखें और पहचानें कि वे कितने समान हैं। आप लिखने से अधिक कोड पढ़ रहे होंगे, और आपको दोनों के साथ समान रूप से सहज होने की आवश्यकता होगी।


7
char * argv [] char ** argv के बराबर 100% है जब इसे एक फ़ंक्शन के पैरामीटर प्रकार के रूप में उपयोग किया जाता है। कोई "कास्ट" शामिल नहीं है, यह भी निहित नहीं है। दोनों ही पात्रों के संकेत बिंदु हैं। आप जो घोषणा करते हैं, उसके संबंध में यह अलग है। लेकिन कंपाइलर एक पॉइंटर को पॉइंटर करने के लिए पैरामीटर के प्रकार को समायोजित करता है, भले ही आपने कहा था कि यह एक सरणी है। इस प्रकार, अनुसरण सभी समान हैं: शून्य च (चार * पी [100]); शून्य च (चार * पी []); शून्य च (चार ** पी);
जोहान्स शाउब - २:२२

4
C89 में (जो अधिकांश लोग उपयोग करते हैं) इस तथ्य का लाभ उठाने का कोई तरीका नहीं है कि आपने इसे एक सरणी के रूप में घोषित किया (इसलिए शब्दार्थ यह कोई फर्क नहीं पड़ता कि आपने सूचक या सरणी घोषित किया है - दोनों को एक के रूप में लिया जाएगा। सूचक)। C99 से शुरू करके, आप इसे एक ऐरे के रूप में घोषित करने से लाभान्वित हो सकते हैं। निम्नलिखित कहता है: "पी हमेशा गैर-अशक्त होता है और कम से कम 100bytes वाले क्षेत्र को इंगित करता है": void f (char p [static 100]); ध्यान दें कि टाइप-वार, पी, अभी भी एक पॉइंटर है।
जोहान्स स्काउब -

5
(विशेष रूप से, & p आपको एक चेर देगा **, लेकिन एक चार नहीं ( ) [100] जो कि यदि p * एक सरणी होगी तो मामला होगा )। मुझे आश्चर्य है कि अभी तक किसी एक उत्तर में उल्लेख नहीं किया गया है। मैं इसे समझना बहुत जरूरी मानता हूं।
जोहान्स शहाब -

व्यक्तिगत रूप से मुझे पसंद है char**क्योंकि यह मुझे याद दिलाता है कि इसे वास्तविक सरणी के रूप में नहीं माना जाना चाहिए, जैसे करना sizeof[arr] / sizeof[*arr]
किरणमाई

9

इससे कोई फर्क नहीं पड़ता है, लेकिन मैं इसका उपयोग करता हूं char *argv[]क्योंकि यह दिखाता है कि चर लंबाई के तार (जो आमतौर पर हैं char *) का एक निश्चित आकार का सरणी है ।



4

यह वास्तव में कोई फर्क नहीं पड़ता है, लेकिन उत्तरार्द्ध अधिक पठनीय है। आपको जो दिया जाता है वह चार बिंदुओं की एक सरणी है, जैसे कि दूसरा संस्करण कहता है। हालाँकि इसे पहले संस्करण की तरह ही एक डबल चार पॉइंटर में बदला जा सकता है।


2

आपको इसे घोषित करने char *argv[]के सभी समान तरीकों के कारण इसे घोषित करना चाहिए , जो इसके सहज अर्थ के सबसे करीब आता है: स्ट्रिंग की एक सरणी।


1

चार ** चरित्र सूचक और चार करने के लिए सूचक * argv [] चरित्र संकेत की सरणी का मतलब है। जैसा कि हम एक सरणी के बजाय सूचक का उपयोग कर सकते हैं, दोनों का उपयोग किया जा सकता है।


0

मुझे दूसरे के बजाय किसी भी दृष्टिकोण का उपयोग करने की कोई विशेष योग्यता नहीं दिखाई देती है - उस सम्मेलन का उपयोग करें जो आपके बाकी कोड के अनुरूप है।


-2

यदि आपको तार की एक अलग या गतिशील संख्या की आवश्यकता होगी, तो चर ** के साथ काम करना आसान हो सकता है। यदि आप स्ट्रिंग की संख्या निर्धारित कर रहे हैं, हालांकि, चार * var [] को प्राथमिकता दी जाएगी।


-2

मुझे पता है कि यह पुराना है, लेकिन अगर आप सी प्रोग्रामिंग भाषा सीख रहे हैं और इसके साथ कुछ भी नहीं कर रहे हैं, तो कमांड-लाइन विकल्पों का उपयोग न करें।

यदि आप कमांड लाइन तर्कों का उपयोग नहीं कर रहे हैं, तो या तो उपयोग न करें। मुख्य समारोह को घोषित करें जैसे कि int main() आप

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

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


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