एक पंक्ति में एक चर घोषित क्यों करें, और इसे अगले में असाइन करें?


101

मैं अक्सर C और C ++ को निम्न सम्मेलन में देखता हूं:

some_type val;
val = something;

some_type *ptr = NULL;
ptr = &something_else;

के बजाय

some_type val = something;
some_type *ptr = &something_else;

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


12
+1 के लिए "मैंने अनुभवी डेवलपर्स की आदतों को इतनी जल्दी खारिज नहीं करना सीखा है।" यह सीखने के लिए एक बुद्धिमान सबक है।
वाइल्डकार्ड

जवाबों:


92

सी

C89 में सभी घोषणाएं एक दायरे ( ) की शुरुआत में होनी थीं{ ... } , लेकिन यह आवश्यकता जल्दी से गिर गई (पहले संकलक एक्सटेंशन के साथ और बाद में मानक के साथ)।

सी ++

ये उदाहरण समान नहीं हैं। डिफॉल्ट कंस्ट्रक्टर और फिर फ़ंक्शन some_type val = something;को val = something;कॉल करते समय कॉपी कंस्ट्रक्टर को कॉल operator=करता है। यह अंतर अक्सर महत्वपूर्ण होता है।

आदतें

कुछ लोग पहले चर घोषित करना पसंद करते हैं और बाद में उन्हें परिभाषित करते हैं, इस मामले में वे अपने कोड को बाद में एक स्थान पर और दूसरे में परिभाषा के साथ सुधार कर रहे हैं।

संकेतकर्ताओं के बारे में, कुछ लोगों को हर सूचक को आरंभ करने की आदत है NULLया nullptrनहीं, चाहे वे उस सूचक के साथ क्या करें।


1
सी ++ के लिए महान अंतर, धन्यवाद। सादे सी के बारे में क्या?
जोनाथन स्टर्लिंग

13
तथ्य यह है कि MSVC अभी भी एक ब्लॉक की शुरुआत में सिवाय घोषणाओं का समर्थन नहीं करता है जब यह सी मोड में संकलन होता है, मेरे लिए अंतहीन जलन का स्रोत है।
माइकल ब्यूर

5
@ मिचेल बर्र: ऐसा इसलिए है क्योंकि MSVC C99 को बिल्कुल भी सपोर्ट नहीं करता है।
orlp

3
"some_type val = something; कॉल कंस्ट्रक्टर को कॉल करता है": इसे कॉपी कंस्ट्रक्टर कह सकते हैं, लेकिन मानक कंपाइलर को किसी टेंपरेरी के डिफॉल्ट-कंस्ट्रक्शन को खत्म करने की अनुमति देता है, वैल की कॉपी का निर्माण और अस्थायी और सीधे निर्माण को नष्ट कर देता है। some_typeकंस्ट्रक्टर somethingएकमात्र तर्क के रूप में ले रहा है। यह C ++ में एक बहुत ही रोचक और असामान्य बढ़त का मामला है ... इसका मतलब है कि इन ऑपरेशनों के अर्थ अर्थ के बारे में अनुमान है।

2
@ एरोविस्टा: अंतर्निहित प्रकारों के लिए वे समान हैं, लेकिन उपयोगकर्ता द्वारा परिभाषित प्रकारों के लिए हमेशा ऐसा नहीं कहा जा सकता है।
orlp

27

आपने अपने प्रश्न C और C ++ को एक ही समय में टैग किया है, जबकि इन भाषाओं में उत्तर काफी अलग है।

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

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

मूल मानक सी भाषा (C89 / 90) में ब्लॉक के बीच में चर घोषित करना गैरकानूनी है, यही कारण है कि आप ब्लॉक की शुरुआत में असमान घोषित (या डमी मूल्यों के साथ आरंभिक) चर देख सकते हैं और फिर सार्थक निर्दिष्ट कर सकते हैं मूल्य बाद में, जब वे सार्थक मूल्य उपलब्ध हो जाते हैं।

C99 भाषा में ब्लॉक के बीच में वैरिएबल घोषित करना ठीक है (जैसे C ++ में), जिसका अर्थ है कि पहला दृष्टिकोण केवल कुछ विशिष्ट स्थितियों में ही आवश्यक है, जब शुरुआती बिंदु को घोषणा के बिंदु पर नहीं जाना जाता है। (यह C ++ पर भी लागू होता है)।


2
@ जोनाथन स्टर्लिंग: मैंने आपके उदाहरण पढ़े। आपको शायद C और C ++ भाषाओं की मानक शब्दावली पर ब्रश करने की आवश्यकता है। विशेष रूप से, शर्तों की घोषणा और परिभाषा पर , जिनका इन भाषाओं में विशिष्ट अर्थ है। मैं इसे फिर से दोहराऊंगा: आपके दोनों उदाहरणों में चर एक पंक्ति में घोषित और परिभाषित हैं। C / C ++ में लाइन some_type val;तुरंत चर को घोषित और परिभाषित करती है val। मेरे जवाब में मेरा यही मतलब था।

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

1
इसलिए, यदि सर्वसम्मति यह है कि "घोषित" गलत शब्द है, तो मैं सुझाव दूंगा कि कोई मेरे साथ मानक के बेहतर ज्ञान के साथ विकीबूक पृष्ठ को संपादित करे।
जोनाथन स्टर्लिंग

2
किसी भी अन्य संदर्भ में घोषित सही शब्द होगा, लेकिन चूंकि घोषणा एक अच्छी तरह से परिभाषित अवधारणा है , परिणाम के साथ, सी और सी ++ में आप इसे अन्य संदर्भों के रूप में शिथिल रूप से उपयोग नहीं कर सकते हैं।
orlp

2
@ybungalobill: आप गलत हैं। C / C ++ में घोषणा और परिभाषा परस्पर अनन्य अवधारणा नहीं है। दरअसल, परिभाषा केवल घोषणा का एक विशिष्ट रूप है । हर परिभाषा एक ही समय में (कुछ अपवादों के साथ) एक घोषणा है। परिभाषित घोषणाएं (यानी परिभाषाएं) और गैर-परिभाषित घोषणाएं हैं। इसके अलावा, आम तौर पर थर्मल डिक्लेरेशन का उपयोग हर समय किया जाता है (भले ही यह एक परिभाषा हो), संदर्भों को छोड़कर जब दोनों के बीच का अंतर महत्वपूर्ण होता है।

13

मुझे लगता है कि यह एक पुरानी आदत है, "स्थानीय घोषणा" से बचा हुआ है। और इसलिए आपके प्रश्न के उत्तर के रूप में: नहीं मुझे नहीं लगता कि एक अच्छा कारण है। मैं खुद ऐसा कभी नहीं करता।


4

मैं में इस बारे में कुछ कहा मेरा उत्तर को Helium3 से एक सवाल

मूल रूप से, मैं कहता हूं कि यह आसानी से देखने के लिए एक दृश्य सहायता है कि क्या बदला है।

if (a == 0) {
    struct whatever *myobject = 0;
    /* did `myobject` (the pointer) get assigned?
    ** or was it `*myobject` (the struct)? */
}

तथा

if (a == 0) {
    struct whatever *myobject;
    myobject = 0;
    /* `myobject` (the pointer) got assigned */
}

4

अन्य उत्तर बहुत अच्छे हैं। C. में यह कुछ इतिहास है। C ++ में कंस्ट्रक्टर और असाइनमेंट ऑपरेटर के बीच अंतर है।

मुझे आश्चर्य है कि कोई भी अतिरिक्त बिंदु का उल्लेख नहीं करता है: घोषणाओं को एक चर के उपयोग से अलग रखना कभी-कभी बहुत अधिक पठनीय हो सकता है।

दृष्टिगत रूप से, कोड पढ़ते समय, अधिक सांसारिक कलाकृतियाँ, जैसे कि चर के प्रकार और नाम, आपके लिए क्या नहीं है। यह कथन है कि आप आमतौर पर सबसे अधिक रुचि रखते हैं, ज्यादातर समय घूरते रहते हैं, और इसलिए बाकी पर नज़र रखने की प्रवृत्ति होती है।

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

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


2

सी में, यह मानक अभ्यास था क्योंकि चर को फ़ंक्शन के प्रारंभ में घोषित किया जाना था, सी ++ के विपरीत, जहां बाद में इसे फ़ंक्शन बॉडी में कहीं भी घोषित किया जा सकता था। पॉइंटर्स को 0 या NULL पर सेट किया गया था, क्योंकि यह सिर्फ यह सुनिश्चित करता है कि पॉइंटर कूड़ेदान की ओर इशारा करता है। अन्यथा, कोई महत्वपूर्ण लाभ नहीं है जो मैं सोच सकता हूं, जो किसी को भी ऐसा करने के लिए मजबूर करता है।


2

स्थानीय चर परिभाषाओं और उनके सार्थक आरंभ के लिए पेशेवरों:

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

  • अधिक कुशल हो सकता है

    • प्रारंभिक मान सेट करने के ओवरहेड्स से बचा जाता है (डिफ़ॉल्ट निर्माण या कुछ प्रहरी मूल्य जैसे NULL)
    • operator= कभी-कभी कम कुशल हो सकता है और एक अस्थायी वस्तु की आवश्यकता होती है
    • कभी-कभी (इनलाइन फ़ंक्शंस के लिए) ऑप्टिमाइज़र कुछ / सभी अक्षमताओं को हटा सकता है

  • बदले में चर के दायरे को कम करने को कम करता है औसत की संख्या समवर्ती दायरे में चर : इस

    • चरों को गुंजाइश में मानसिक रूप से ट्रैक करना आसान बनाता है , निष्पादन प्रवाह और कथन जो उन चर को प्रभावित कर सकते हैं, और उनके मूल्य का आयात कर सकते हैं
    • कम से कम कुछ जटिल और अपारदर्शी वस्तुओं के लिए, यह कार्यक्रम के संसाधन उपयोग (ढेर, धागे, साझा मेमोरी, डिस्क्रिप्टर) को कम करता है
  • कभी-कभी अधिक संक्षिप्त रूप में जब आप परिभाषा में चर नाम को दोहरा नहीं रहे होते हैं तो एक प्रारंभिक सार्थक असाइनमेंट में

  • कुछ प्रकार के लिए आवश्यक है जैसे कि संदर्भ और जब आप चाहते हैं कि वस्तु हो const

चर परिभाषाओं को समूहीकृत करने के लिए तर्क:

  • कभी-कभी यह चर के प्रकार के कारक को सुविधाजनक और / या संक्षिप्त करता है:

    the_same_type v1, v2, v3;

    (यदि कारण सिर्फ इतना है कि प्रकार नाम अत्यधिक लंबा या जटिल है, तो typedefकभी-कभी बेहतर हो सकता है)

  • कभी-कभी कुछ कार्यों में शामिल चर (और प्रकार) के सेट पर जोर देने के लिए उनके उपयोग से स्वतंत्र रूप से समूह चर के लिए यह वांछनीय है:

    type v1;
    type v2; type v3;

    यह प्रकार की समानता पर जोर देता है और उन्हें बदलना आसान बनाता है, जबकि अभी भी प्रति पंक्ति एक चर से चिपका हुआ है जो कॉपी-पेस्ट, //टिप्पणी आदि की सुविधा देता है।

जैसा कि प्रोग्रामिंग में अक्सर होता है, जबकि अधिकांश स्थितियों में एक अभ्यास के लिए स्पष्ट अनुभवजन्य लाभ हो सकता है, अन्य अभ्यास वास्तव में कुछ मामलों में बेहतर हो सकता है।


मैं चाहता हूं कि अधिक भाषाएं उस मामले को भेद करें जहां कोड घोषित होता है और एक चर के मूल्य को निर्धारित करता है जिसे कभी भी कहीं और नहीं लिखा जाएगा, हालांकि नए चर समान नाम का उपयोग कर सकते हैं [अर्थात जहां व्यवहार वही होगा जहां बाद के बयानों ने उसी चर का उपयोग किया है या एक अलग]], जहां से कोड एक वैरिएबल बनाता है जो कई स्थानों पर लिखने योग्य होना चाहिए। जबकि दोनों उपयोग के मामले उसी तरह निष्पादित होंगे, जब बग को ट्रैक करने का प्रयास करते समय यह जानना कि चर कब बदल सकते हैं, बहुत उपयोगी है।
सुपरकैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.