प्रकार के बाद के बजाय चर नाम से पहले तारांकन क्यों किया जाता है?


187

अधिकांश सी प्रोग्रामर इस तरह चर नाम क्यों देते हैं:

int *myVariable;

इस तरह के बजाय:

int* myVariable;

दोनों मान्य हैं। यह मुझे लगता है कि तारांकन प्रकार का एक हिस्सा है, चर नाम का हिस्सा नहीं है। क्या कोई इस तर्क की व्याख्या कर सकता है?


1
दूसरी शैली सामान्य रूप से अधिक सहज लगती है, लेकिन पूर्व कोड में संबंधित-संबंधित बग से बचने का रास्ता है। यदि आप वास्तव में बाद की शैली से जुड़े हुए हैं, तो आप हमेशा साथ रह सकते हैं typedefs, लेकिन यह अनावश्यक जटिलता, आईएमएचओ को जोड़ देगा।
क्लाउड

जवाबों:


250

वे बिल्कुल बराबर हैं। हालाँकि, में

int *myVariable, myVariable2;

यह स्पष्ट लगता है कि myVariable के पास int * है , जबकि myVariable2 का प्रकार int है । में

int* myVariable, myVariable2;

यह स्पष्ट लग सकता है कि दोनों प्रकार int * के हैं , लेकिन यह सही नहीं है क्योंकि myVariable2प्रकार int है

इसलिए, पहली प्रोग्रामिंग शैली अधिक सहज है।


135
शायद, लेकिन मैं एक घोषणा में मिक्स और मैच प्रकार नहीं होगा।
बॉबीशाफ्टो

15
@ बॉबीशैफ्टो सहमत। यहां तक ​​कि हर तर्क को पढ़ने के बाद भी, मैं int* someVarव्यक्तिगत परियोजनाओं के लिए चिपका रहा हूं । यह अधिक समझ में आता है।
एलिसा हरॉल्डसन

30
@ कुप्पीकोस केवल तब तक अधिक समझ में आता है जब तक कि आप सी के घोषणा सिंटैक्स को "घोषणाओं के उपयोग के आधार पर" नहीं सीख लेते। घोषणाएँ ठीक उसी वाक्यविन्यास का उपयोग करती हैं जो समान-टाइप किए गए चर का उपयोग करते हैं। जब आप एक प्रकार की स्याही की घोषणा करते हैं, तो ऐसा नहीं लगता है int[10] x:। यह केवल C का सिंटैक्स नहीं है। व्याकरण स्पष्ट रूप से इस प्रकार परिकल्पित करता है: int (*x)और नहीं के रूप में (int *) x, इसलिए बाईं ओर तारांकन करना केवल भ्रामक है और सी घोषणा सिंटैक्स की गलतफहमी पर आधारित है।
पीकर

8
सुधार: इसलिए, आपको कभी भी एक लाइन पर एक से अधिक वेरिएबल की घोषणा नहीं करनी चाहिए। सामान्य तौर पर, आपको कुछ अन्य असंबंधित, खराब और खतरनाक कोडिंग शैली के आधार पर एक निश्चित कोडिंग शैली को प्रेरित नहीं करना चाहिए।
लंडिन

6
यही कारण है कि मैं प्रति सूचक घोषणा के लिए एक चर के लिए छड़ी। यदि आप इसके int* myVariable; int myVariable2;बजाय करते हैं तो बिल्कुल कोई भ्रम नहीं है ।
पैट्रिक रॉबर्ट्स

122

यदि आप इसे दूसरे तरीके से देखते हैं, तो यह *myVariableप्रकार का है int, जो कुछ समझ में आता है।


6
यह मेरा पसंदीदा स्पष्टीकरण है, और अच्छी तरह से काम करता है क्योंकि यह सामान्य रूप से सी की घोषणा quirks बताता है - यहां तक ​​कि घृणित और gnarly फ़ंक्शन पॉइंटर सिंटैक्स।
बेंजामिन पोलाक

12
यह साफ-सुथरा है, क्योंकि आप सोच सकते हैं कि कोई वास्तविक पॉइंटर प्रकार नहीं है। केवल ऐसे चर होते हैं, जब उचित रूप से संदर्भित या डीएफ़रेंड किया जाता है, जो आपको एक आदिम प्रकार देता है।
बॉयोन्ज़िन

1
दरअसल, '* myVariable' टाइप NULL हो सकता है। मामले को बदतर बनाने के लिए यह सिर्फ एक यादृच्छिक मेमोरी मेमोरी स्थान हो सकता है।
चिनफ

4
qonf: NULL एक प्रकार नहीं है। myVariableNULL हो सकता है, जिस स्थिति *myVariableमें विभाजन दोष का कारण बनता है, लेकिन NULL का कोई प्रकार नहीं है।
डैनियल रोथ्लिसबर्गर

28
यह बिंदु इस तरह के संदर्भ में भ्रामक हो सकता है: int x = 5; int *pointer = &x;क्योंकि यह सुझाव देता है कि हम स्वयं को *pointerनहीं बल्कि कुछ मूल्य के लिए इंट सेट करते हैं pointer
rafalcieslak

40

क्योंकि * उस लाइन में टाइप करने के लिए चर की तुलना में अधिक बारीकी से बांधता है:

int* varA, varB; // This is misleading

जैसा कि @ लुंडिन नीचे बताते हैं, कास्ट सोचने के लिए और भी सूक्ष्मता जोड़ता है। आप प्रति पंक्ति एक चर घोषित करके इसे पूरी तरह से समाप्त कर सकते हैं, जो कभी अस्पष्ट नहीं होता है:

int* varA;
int varB;

स्पष्ट कोड और संक्षिप्त कोड के बीच संतुलन बनाना मुश्किल है - एक दर्जन से अधिक अनावश्यक लाइनें int a;अच्छी नहीं हैं। फिर भी, मैं प्रति पंक्ति एक घोषणा को डिफ़ॉल्ट करता हूं और बाद में कोड के संयोजन के बारे में चिंता करता हूं।


2
खैर, भ्रामक पहला उदाहरण मेरी आँखों में एक डिज़ाइन त्रुटि है। यदि मैं कर सकता था, तो मैं सी से घोषणा के उस तरीके को पूरी तरह से हटा दूंगा, और इसे दोनों प्रकार के इंट * के रूप में बनाया।
एडम बजगर

1
"* प्रकार की तुलना में चर के अधिक निकटता से बांधता है" यह एक भोली तर्क है। विचार करें int *const a, b;। * "बाँध" कहाँ है? प्रकार aहै int* const, तो आप यह कैसे कह सकते हैं कि * चर का है जब यह स्वयं प्रकार का हिस्सा है?
लुंडिन

1
प्रश्न के लिए विशिष्ट, या सभी मामलों को कवर: एक चुनें। मैं एक नोट करूँगा, लेकिन यह मेरे अंतिम सुझाव के लिए एक और अच्छा तर्क है: प्रति पंक्ति एक घोषणा इस गड़बड़ करने के अवसरों को कम करती है।
ojrac

36

यहाँ अब तक किसी ने भी उल्लेख नहीं किया है कि यह तारांकन वास्तव में सी में " डीरेफेरेंस ऑपरेटर " है।

*a = 10;

रेखा से ऊपर मतलब यह नहीं है कि मैं प्रदान करना चाहते हैं 10के लिए a, इसका मतलब है मैं प्रदान करना चाहते हैं 10जो कुछ भी स्मृति स्थान के लिए aकरने के लिए अंक। और मैंने कभी किसी को लिखते नहीं देखा

* a = 10;

क्या तुम? तो dereference ऑपरेटर बहुत अधिक हमेशा एक स्थान के बिना लिखा जाता है। यह संभवतया इसे कई रेखाओं में विभाजित गुणन से अलग करना है:

x = a * b * c * d
  * e * f * g;

यहाँ *eभ्रामक होगा, है ना?

ठीक है, अब निम्नलिखित पंक्ति का वास्तव में क्या मतलब है:

int *a;

ज्यादातर लोग कहेंगे:

इसका मतलब है कि मूल्य के aलिए एक सूचक है int

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

का मान मान aप्रकार का है int

तो वास्तव में इस घोषणा में तारांकन को एक परिचालक ऑपरेटर के रूप में भी देखा जा सकता है, जो इसके प्लेसमेंट की व्याख्या भी करता है। और यह aएक ऐसा संकेतक है जिसे वास्तव में घोषित नहीं किया गया है, यह इस तथ्य से निहित है, कि केवल एक चीज जिसे आप वास्तव में संक्षिप्त कर सकते हैं वह एक सूचक है।

सी मानक केवल *ऑपरेटर को दो अर्थों को परिभाषित करता है :

  • अप्रत्यक्ष परिचालक
  • गुणक संचालक

और अप्रत्यक्षता केवल एक ही अर्थ है, एक सूचक घोषित करने के लिए कोई अतिरिक्त अर्थ नहीं है, बस अप्रत्यक्षता है, जो कि डेरेफेरेंस ऑपरेशन करता है, वह एक अप्रत्यक्ष पहुंच करता है, इसलिए int *a;इस तरह के एक बयान के भीतर भी एक अप्रत्यक्ष पहुंच है ( *साधन) अप्रत्यक्ष पहुंच) और इस प्रकार ऊपर दिया गया दूसरा कथन पहले वाले मानक के बहुत करीब है।


6
मुझे लिखने से बचाने के लिए धन्यवाद यहाँ एक और उत्तर। BTW मैं आमतौर पर int a, *b, (*c)();कुछ इस तरह पढ़ता हूं जैसे "निम्नलिखित वस्तुओं को घोषित करें int: ऑब्जेक्ट a, द्वारा इंगित की गई bवस्तु, और ऑब्जेक्ट द्वारा इंगित किए गए फ़ंक्शन से लौटा c"।
अंती हापाला

1
*में int *a;एक ऑपरेटर नहीं है, और यह अपसंदर्भन नहीं है a(जो भी अभी तक परिभाषित नहीं है)
एम एम

1
@MM कृपया किसी भी ISO C मानक का नाम और पंक्ति संख्या नाम दें जहाँ यह मानक कहता है कि तारांकन गुणन या अप्रत्यक्ष के अलावा कुछ और हो सकता है। यह केवल उदाहरण के लिए "सूचक घोषणा" दिखाता है, यह कहीं भी तारांकन के लिए तीसरे अर्थ को परिभाषित करता है (और यह कोई अर्थ नहीं परिभाषित करता है, यह एक ऑपरेटर नहीं होगा)। ओह, मैंने कहीं भी दावा नहीं किया है कि कुछ भी "परिभाषित" है, आपने इसे बना लिया है। या जैसा कि जोनाथन लेफ़लर ने इसे सी मानक में रखा है, * हमेशा "व्याकरण" होता है, यह सूचीबद्ध घोषणाओं का हिस्सा नहीं है (इसलिए यह घोषणा का हिस्सा नहीं है, इस प्रकार यह एक ऑपरेटर होना चाहिए)
मेक्सी

1
@MM 6.7.6.1 केवल, उदाहरण के द्वारा घोषणा इसकी सटीक जानकारी की तरह मैंने कहा, यह कोई देता है अर्थ के लिए *(यह करने के लिए नहीं, कुल अभिव्यक्ति के लिए केवल एक अर्थ देता है *अभिव्यक्ति के भीतर!)। यह कहता है कि "int a;" एक पॉइंटर घोषित करता है, जो यह करता है, कभी भी दावा नहीं करता है, लेकिन बिना दिए गए अर्थ के बिना *, इसे * के रूप में पढ़ा गया मान aहै एक int अभी भी पूरी तरह से वैध है, क्योंकि इसका वही वास्तविक अर्थ है। कुछ भी नहीं, वास्तव में 6.7.6.1 में लिखा गया कुछ भी उस कथन का खंडन नहीं करेगा।
मैकी

2
कोई "कुल अभिव्यक्ति" नहीं है। int *a;एक घोषणा है, एक अभिव्यक्ति नहीं है। aद्वारा डिरेल्ड नहीं किया गया है int *a;aअभी भी अस्तित्व में नहीं है इस बिंदु पर *संसाधित किया जा रहा है। क्या आपको लगता int *a = NULL;है कि यह एक बग है क्योंकि यह एक अशक्त सूचक है?
एमएम

17

मैं यहाँ एक अंग पर बाहर जाना और कहते हैं कि करने के लिए जा रहा हूँ इस सवाल का एक सीधा जवाब है वहाँ चर घोषणाओं के लिए और पैरामीटर और बदले प्रकार के लिए दोनों, है, जो है जो एस्टरिस्क नाम के आगे जाना चाहिए: int *myVariable;। इसकी सराहना करने के लिए, यह देखें कि आप C में अन्य प्रकार के प्रतीक कैसे घोषित करते हैं:

int my_function(int arg); एक समारोह के लिए;

float my_array[3] एक सरणी के लिए।

सामान्य पैटर्न, जिसे घोषणा के रूप में उपयोग किया जाता है, कहा जाता है कि प्रतीक का प्रकार नाम से पहले भाग में विभाजित है, और नाम के आसपास के हिस्से , और नाम के आसपास के ये हिस्से उस वाक्य की नकल करते हैं जिसका उपयोग आप एक पाने के लिए करेंगे। बाईं ओर के प्रकार का मूल्य:

int a_return_value = my_function(729);

float an_element = my_array[2];

और: int copy_of_value = *myVariable;

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


12

यह सिर्फ वरीयता का मामला है।

जब आप कोड पढ़ते हैं, तो दूसरे मामले में चर और बिंदुओं के बीच अंतर करना आसान होता है, लेकिन इससे भ्रम हो सकता है जब आप एक ही लाइन में एक सामान्य प्रकार के चर और संकेत दोनों डाल रहे हैं (जो कि अक्सर परियोजना दिशानिर्देशों द्वारा हतोत्साहित किया जाता है) क्योंकि पठनीयता घट जाती है)।

मैं टाइप नाम, उदाहरण के लिए उनके संबंधित संकेत के साथ संकेत देने की घोषणा करना पसंद करता हूं

int* pMyPointer;

3
सवाल सी के बारे में है, जहां कोई संदर्भ नहीं हैं।
अंती हापाला

यह इंगित करने के लिए धन्यवाद, हालांकि प्रश्न संकेत या संदर्भ के बारे में नहीं था, लेकिन कोड स्वरूपण के बारे में, मूल रूप से।
मैकबर्डी

8

एक महान गुरु ने एक बार कहा था "इसे संकलक का तरीका पढ़ें, आपको अवश्य करना चाहिए।"

http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835

यह निर्धारित कॉन्स्ट प्लेसमेंट के विषय पर था, लेकिन यहां भी वही नियम लागू होता है।

संकलक इसे पढ़ता है:

int (*a);

जैसा नहीं:

(int*) a;

यदि आपको चर के बगल में स्टार रखने की आदत है, तो यह आपकी घोषणाओं को पढ़ने में आसान बना देगा। यह भी इस तरह से आंखों की रोशनी से बचा जाता है:

int* a[10];

- संपादित करें -

वास्तव में व्याख्या करने के लिए मैं क्या मतलब है जब मैं कहता हूँ यह के रूप में पार्स है int (*a), इसका मतलब है कि *अधिक करने के लिए कसकर बांध aसे यह करता है करने के लिए int, बहुत ज्यादा ढंग से कि अभिव्यक्ति में 4 + 3 * 7 3अधिक करने के लिए कसकर बांध 7से यह करता है करने के लिए 4की उच्च पूर्वता के कारण *

एससीआई कला के लिए माफी के साथ, पार्सिंग के लिए एएसटी का एक सारांश int *aलगभग इसी तरह दिखता है:

      Declaration
      /         \
     /           \
Declaration-      Init-
Secifiers       Declarator-
    |             List
    |               |
    |              ...
  "int"             |
                Declarator
                /       \
               /        ...
           Pointer        \
              |        Identifier
              |            |
             "*"           |
                          "a"

जैसा कि स्पष्ट रूप से दिखाया गया है, उनके सामान्य पूर्वज के बाद से *अधिक मजबूती से बांधता aहै Declarator, जबकि आपको Declarationएक आम पूर्वज को खोजने के लिए पेड़ के ऊपर जाने की आवश्यकता होती है जिसमें शामिल है int


नहीं, संकलक निश्चित रूप से प्रकार को पढ़ता है (int*) a
लुंडिन

@ लुंडिन यह सबसे बड़ी गलतफहमी है कि अधिकांश आधुनिक सी ++ प्रोग्रामर हैं। एक चर घोषणा पार्सिंग कुछ इस तरह से चला जाता है। चरण 1. पहला टोकन पढ़ें, जो घोषणा का "आधार प्रकार" बन जाता है। intइस मामले में। चरण 2. किसी भी प्रकार की सजावट सहित एक घोषणा पढ़ें। *aइस मामले में। चरण 3 अगला चरित्र पढ़ें। यदि अल्पविराम, इसका उपभोग करें और चरण 2 पर वापस जाएं। यदि अर्धविराम बंद हो जाता है। अगर कुछ और एक वाक्यविन्यास त्रुटि फेंक देते हैं। ...
डॉग्फ

@ लुंडिन ... यदि पार्सर इसे आपके द्वारा सुझाए जा रहे तरीके को पढ़ता है, तो हम कुछ बिंदुओं को लिखने int* a, b;और प्राप्त करने में सक्षम होंगे । मैं जो बिंदु बना रहा हूं वह यह है कि *चर के साथ बांधता है और इसके साथ पार्स किया जाता है, घोषणा के "आधार प्रकार" बनाने के प्रकार के साथ नहीं। यह इस कारण का भी हिस्सा है कि टाइपराइफ को कुछ बिंदुओं को typedef int *iptr; iptr a, b;बनाने की अनुमति देने के लिए पेश किया गया था । एक typedef का उपयोग कर आप कर सकते हैं के लिए बाध्य *करने के लिए int
दिनांक

1
... निश्चित रूप से, संकलक "को जोड़ती है" से आधार प्रकार Declaration-Specifier"सजावट 'के साथ साथ Declaratorप्रत्येक चर के लिए अंतिम प्रकार पर पहुंचने के लिए। हालांकि यह घोषणा को निर्दिष्ट करने के लिए सजावट को "स्थानांतरित" नहीं करता है अन्यथा int a[10], b;यह पूरी तरह से हास्यास्पद परिणाम देगा, int *a, b[10];जैसा कि यह बताता है int *a , b[10] ;। इसका वर्णन करने का कोई अन्य तरीका नहीं है जो समझ में आता है।
dgnuff

1
हाँ ठीक है, यहाँ महत्वपूर्ण भाग संकलक पार्सिंग का वाक्यविन्यास या क्रम नहीं है, लेकिन यह है कि int*aअंत में संकलक द्वारा पढ़ा गया है: " aटाइप int*"। यही मेरा मूल टिप्पणी के साथ मतलब था।
लंडिन

3

क्योंकि जब आपके पास घोषणाएं होती हैं तो यह अधिक समझ में आता है:

int *a, *b;

5
यह वस्तुतः प्रश्न से भीख माँगने का एक उदाहरण है। नहीं, यह इस तरह से अधिक समझ में नहीं आता है। "int * a, b" बस दोनों को इंगित कर सकता है।
माइकलगैग

1
@MichaelGG तुम वहाँ कुत्ते wagging पूंछ मिल गया है। निश्चित रूप से K & R निर्दिष्ट कर सकता है int* a, b;जिसने bएक सूचक को बनाया है int। लेकिन उन्होंने ऐसा नहीं किया। और अच्छे कारण के साथ। आपकी प्रस्तावित प्रणाली के तहत bनिम्नलिखित घोषणा में क्या प्रकार है int* a[10], b;:?
१६:

2

एक पंक्ति में कई बिंदुओं की घोषणा करने के लिए, मैं पसंद int* a, * b;करता हूं जो अधिक सहज ज्ञान युक्त रूप से "a" को पूर्णांक के लिए एक संकेतक के रूप में घोषित करता है, और जब "b" की घोषणा करता है तो शैलियों का मिश्रण नहीं करता है। जैसे किसी ने कहा, मैं वैसे भी एक ही बयान में दो अलग-अलग प्रकार की घोषणा नहीं करता।


2

जो लोग पसंद करते हैं int* x; हैं वे अपने कोड को एक काल्पनिक दुनिया में लागू करने की कोशिश कर रहे हैं, जहां प्रकार बाईं तरफ है और पहचानकर्ता (नाम) दाईं ओर है।

मैं कहता हूँ "काल्पनिक" क्योंकि:

सी और सी ++ में, सामान्य मामले में, घोषित पहचानकर्ता प्रकार की जानकारी से घिरा हुआ है।

यह पागल लग सकता है, लेकिन आप जानते हैं कि यह सच है। यहाँ कुछ उदाहरण हैं:

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

  • void (*fn)(int)साधन fnएक फ़ंक्शन के लिए एक संकेतक है जो एक लेता है intऔर कुछ भी नहीं देता है।

  • int a[10]10 के एक सरणी के रूप में 'ए' घोषित करता है int

  • pixel bitmap[height][width]

  • स्पष्ट रूप से, मैंने चेरी से चुने हुए उदाहरण दिए हैं जिनमें मेरी बात करने के अधिकार पर बहुत अधिक प्रकार की जानकारी है। बहुत सारे घोषणाएं हैं जहां सबसे - यदि नहीं - तो सभी प्रकार बाईं ओर है, जैसे struct { int x; int y; } center

यह घोषणा सिंटैक्स K & R की घोषणाओं से उत्पन्न हुआ, जिसमें घोषणाओं के उपयोग को दर्शाया गया था। सरल घोषणाओं को पढ़ना सहज है, और अधिक जटिल लोगों को पढ़ने से दाएं-बाएं-दाएं नियम (कभी-कभी सर्पिल नियम या बस दाएं-बाएं नियम) को सीखने में महारत हासिल की जा सकती है।

सी पर्याप्त सरल है कि कई सी प्रोग्रामर इस शैली को गले लगाते हैं और सरल घोषणाएं लिखते हैं int *p

C ++ में, सिंटैक्स को थोड़ा और अधिक जटिल (वर्गों, संदर्भों, टेम्प्लेट, एनम वर्ग के साथ) मिला, और, उस जटिलता की प्रतिक्रिया के रूप में, आप कई घोषणाओं में पहचानकर्ता से प्रकार को अलग करने में अधिक प्रयास देखेंगे। दूसरे शब्दों में, int* pयदि आप C ++ कोड के बड़े स्वैथ की जाँच करते हैं, तो आपको अधिक- शैली की घोषणाएँ दिखाई दे सकती हैं ।

किसी भी भाषा में, आप हमेशा एक ही कथन में कई चर घोषित करके, (2) चर चर के बाईं ओर टाइप कर सकते हैं , और (2) एस (या उर्फ ​​घोषणाओं का उपयोग करते हैं , जो विडंबना यह है कि उपनाम डालते हैं) प्रकार के बाईं ओर पहचानकर्ता)। उदाहरण के लिए:typedef

typedef int array_of_10_ints[10];
array_of_10_ints a;

छोटा सवाल, क्यों शून्य (* fn) (int) का अर्थ नहीं हो सकता है "fn एक ऐसा फंक्शन है जो एक int को स्वीकार करता है, और रिटर्न (void *)?
सोहम डोंगरगांवकर

1
@ a3y3: क्योंकि कोष्ठक (*fn)पॉइंटर fnको रिटर्न प्रकार के बजाय संबंधित के साथ रखता है ।
एड्रियन मैक्कार्थी

समझ गया। मुझे लगता है कि आपके उत्तर में जोड़ा जाना काफी महत्वपूर्ण है, मैं जल्द ही एक संपादन का सुझाव दूंगा।
सोहम

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

1

मैंने पहले से ही सीपी में एक समान प्रश्न का उत्तर दिया था, और, क्योंकि किसी ने भी उल्लेख नहीं किया है, यहां भी मुझे यह बताना होगा कि सी एक स्वतंत्र प्रारूप भाषा है , जो भी शैली आप ठीक चुनते हैं जबकि पार्सर प्रत्येक टोकन का एक अंतर बना सकता है। सी की यह ख़ासियत एक बहुत ही विशेष प्रकार की प्रतियोगिता का नेतृत्व करती है जिसे सी ऑबफैक्शन प्रतियोगिता कहा जाता है


1

इस विषय में बहुत सारे तर्क सादे व्यक्तिपरक हैं और "स्टार चर नाम से बांधता है" के बारे में तर्क भोला है। यहाँ कुछ तर्क दिए गए हैं जो केवल राय नहीं हैं:


भूल गए सूचक प्रकार क्वालिफायर

औपचारिक रूप से, "तारा" न तो प्रकार का है और न ही चर नाम का है, यह पॉइंटर नाम की अपनी स्वयं की व्याकरणिक वस्तु का हिस्सा है । औपचारिक सी वाक्य रचना (आईएसओ 9899: 2018) है:

(6.7) घोषणा:
घोषणा-विनिर्देशक init-घोतक-सूची विकल्प ;

जहाँ डिक्लेरेशन-स्पेसियर्स में टाइप (और स्टोरेज) होता है, और इनिट-डिक्लेरेटर-लिस्ट में पॉइंटर और वेरिएबल का नाम होता है। यदि हम देखते हैं कि हम इस घोषणा सूची वाक्यविन्यास को और अधिक विच्छेदित करते हैं:

(6.7.6) घोषणाकर्ता :
पॉइंटर ऑप्ट डायरेक्ट- डिक्लेटर
...
(6.7.6) पॉइंटर:
* टाइप-क्वालिफायर-लिस्ट ऑप्ट
* टाइप-क्वालिफायर-लिस्ट ऑप्ट पॉइंटर

जहां एक घोषणाकर्ता पूरी घोषणा है, एक प्रत्यक्ष-घोषणाकर्ता पहचानकर्ता (चर नाम) है, और एक सूचक वह तारा है जिसके बाद एक वैकल्पिक प्रकार का गुणक होता है जो सूचक से संबंधित होता है।

"स्टार वैरिएबल के अंतर्गत आता है" के बारे में विभिन्न शैली तर्क देता है, यह है कि वे इन पॉइंटर प्रकार के क्वालीफायर के बारे में भूल गए हैं। int* const x, int *const xयाint*const x ?

पर विचार करें int *const a, b;, के प्रकार क्या हैं aऔर b? इतना स्पष्ट नहीं है कि "स्टार किसी भी लंबे समय तक चर" के अंतर्गत आता है। इसके बजाय, कोई विचार करना शुरू कर देगा कि उसका constसंबंध कहां है।

आप निश्चित रूप से एक ध्वनि तर्क दे सकते हैं कि स्टार पॉइंटर प्रकार के क्वालीफायर से संबंधित है, लेकिन उससे परे नहीं।

पॉइंटर के लिए टाइप क्वालिफायर सूची int *aशैली का उपयोग करने वालों के लिए समस्या पैदा कर सकती है । जो अंदर एक बिंदु का उपयोग करते हैं typedef(जो हमें नहीं करना चाहिए, बहुत बुरा अभ्यास!) और लगता है कि "स्टार चर नाम से संबंधित है" यह बहुत सूक्ष्म बग लिखने के लिए करते हैं:

    /*** bad code, don't do this ***/
    typedef int *bad_idea_t; 
    ...
    void func (const bad_idea_t *foo);

यह साफ-साफ संकलन करता है। अब आप सोच सकते हैं कि कोड को सही बनाया गया है। ऐसा नहीं! यह कोड गलती से एक फेक कॉन्स्टेंस है।

प्रकार fooवास्तव में है int*const*- बाहरी सबसे अधिक सूचक को केवल पढ़ने के लिए बनाया गया था, न कि आंकड़ों पर बताया गया है। तो इस फ़ंक्शन के अंदर हम कर सकते हैं **foo = n;और यह कॉलर में परिवर्तनशील मान को बदल देगा।

इसका कारण यह है कि अभिव्यक्ति में const bad_idea_t *foo, *यहाँ चर नाम नहीं है! छद्म कोड में, इस पैरामीटर घोषणा को पढ़ा जाना चाहिए const (bad_idea_t *) fooऔर जैसा नहीं(const bad_idea_t) *foo । स्टार इस मामले में छिपे हुए पॉइंटर प्रकार का है - प्रकार एक पॉइंटर है और एक कास्ट-योग्य पॉइंटर के रूप में लिखा गया है *const

लेकिन फिर उपरोक्त उदाहरण में समस्या की जड़ शैली के पीछे बिंदुओं को छिपाने की प्रथा है typedefन कि *शैली के पीछे ।


एक ही लाइन पर कई चर की घोषणा के संबंध में

एक ही पंक्ति में कई चरों को घोषित करना व्यापक रूप से बुरे अभ्यास 1) के रूप में पहचाना जाता है । CERT-C इसे अच्छी तरह बताता है:

DCL04 सी। प्रति घोषणा एक से अधिक चर घोषित न करें

बस पढ़ने अंग्रेजी, तो सामान्य ज्ञान इससे सहमत हैं कि एक घोषणा किया जाना चाहिए एक घोषणा।

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

तो प्रोग्रामर के बारे में भ्रमित होने का तर्क int* a, bबुरा है। समस्या की जड़ कई घोषणाओं का उपयोग है, न कि प्लेसमेंट *। शैली के बावजूद, आपको इसके बजाय लिखना चाहिए:

    int* a; // or int *a
    int b;

एक और ध्वनि लेकिन व्यक्तिपरक तर्क यह होगा कि बिना प्रश्न int* aके प्रकार दिया गया aहैint* और इसलिए तारा प्रकार के क्वालीफायर के साथ है।

लेकिन मूल रूप से मेरा निष्कर्ष यह है कि यहाँ पोस्ट किए गए कई तर्क सिर्फ व्यक्तिपरक और भोले हैं। आप वास्तव में या तो शैली के लिए एक वैध तर्क नहीं दे सकते हैं - यह वास्तव में व्यक्तिपरक व्यक्तिगत प्राथमिकता का मामला है।


1) CERT-C DCL04-C


आश्चर्यजनक रूप से, यदि आप उस लेख को पढ़ते हैं जिससे मैं जुड़ा हुआ हूं, तो इस विषय पर एक छोटा खंड है, typedef int *bad_idea_t; void func(const bad_idea_t bar); क्योंकि महान पैगंबर दान सक्स सिखाते हैं "यदि आप हमेशा constजहां तक ​​संभव हो सके दाईं ओर स्थान रखें, बिना अर्थ का अर्थ बदले" यह पूरी तरह से समाप्त हो जाता है। एक मुद्दा बनने के लिए। यह आपकी constघोषणाओं को पढ़ने के लिए अधिक सुसंगत बनाता है । "कॉन्स्ट शब्द के दाईं ओर सब कुछ है जो कि कॉन्स्ट है। बाईं ओर सब कुछ इसका प्रकार है।" यह एक घोषणा में सभी कास्ट पर लागू होगा। इसके साथ आज़माएंint const * * const x;
जुगनू

-1

विचार करें

int *x = new int();

यह अनुवाद नहीं करता है

int *x;
*x = new int();

यह वास्तव में अनुवाद करता है

int *x;
x = new int();

यह int *xअंकन को कुछ असंगत बनाता है ।


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