यह उत्कृष्ट अभ्यास है।
लूप के अंदर चर बनाकर, आप यह सुनिश्चित करते हैं कि लूप के अंदर उनका दायरा सीमित है। इसे न तो संदर्भित किया जा सकता है और न ही लूप के बाहर बुलाया जा सकता है।
इस तरफ:
यदि चर का नाम थोड़ा "सामान्य" है (जैसे "i"), तो बाद में आपके कोड में उसी नाम के किसी अन्य चर के साथ इसे मिलाने का कोई जोखिम नहीं है ( -Wshadow
जीसीसी पर चेतावनी निर्देश का उपयोग करके इसे कम किया जा सकता है )
कंपाइलर जानता है कि चर गुंजाइश लूप के अंदर तक ही सीमित है, और इसलिए चर को गलती से संदर्भित किसी अन्य स्थान पर होने पर एक उचित त्रुटि संदेश जारी करेगा।
अंतिम लेकिन कम से कम, कुछ समर्पित अनुकूलन को कंपाइलर (सबसे महत्वपूर्ण रूप से पंजीकरण आवंटन) द्वारा अधिक कुशलता से किया जा सकता है, क्योंकि यह जानता है कि चर का उपयोग लूप के बाहर नहीं किया जा सकता है। उदाहरण के लिए, बाद में पुन: उपयोग के लिए परिणाम को संग्रहीत करने की आवश्यकता नहीं है।
संक्षेप में, आप इसे करने के लिए सही हैं।
ध्यान दें कि चर प्रत्येक लूप के बीच अपने मान को बनाए रखने के लिए नहीं है । ऐसे मामले में, आपको हर बार इसे शुरू करने की आवश्यकता हो सकती है। आप लूप को घेरते हुए एक बड़ा ब्लॉक भी बना सकते हैं, जिसका एकमात्र उद्देश्य चरों को घोषित करना है जो एक लूप से दूसरे में अपने मूल्य को बनाए रखना होगा। इसमें आमतौर पर लूप काउंटर ही शामिल होता है।
{
int i, retainValue;
for (i=0; i<N; i++)
{
int tmpValue;
/* tmpValue is uninitialized */
/* retainValue still has its previous value from previous loop */
/* Do some stuff here */
}
/* Here, retainValue is still valid; tmpValue no longer */
}
प्रश्न # 2 के लिए: चर को एक बार आवंटित किया जाता है, जब फ़ंक्शन कहा जाता है। वास्तव में, आवंटन के दृष्टिकोण से, यह (लगभग) फ़ंक्शन की शुरुआत में चर को घोषित करने के समान है। एकमात्र अंतर गुंजाइश है: लूप के बाहर चर का उपयोग नहीं किया जा सकता है। यह भी संभव हो सकता है कि चर आवंटित नहीं किया गया है, बस कुछ मुफ्त स्लॉट (दूसरे चर से जिसका दायरा समाप्त हो गया है) का फिर से उपयोग कर रहा है।
प्रतिबंधित और अधिक सटीक गुंजाइश के साथ अधिक सटीक अनुकूलन आते हैं। लेकिन इससे भी महत्वपूर्ण बात, यह आपके कोड को सुरक्षित बनाता है, कम राज्यों (यानी चर) के साथ जब कोड के अन्य हिस्सों को पढ़ने के बारे में चिंता करता है।
यह एक if(){...}
ब्लॉक के बाहर भी सच है । आमतौर पर, इसके बजाय:
int result;
(...)
result = f1();
if (result) then { (...) }
(...)
result = f2();
if (result) then { (...) }
यह लिखना अधिक सुरक्षित है:
(...)
{
int const result = f1();
if (result) then { (...) }
}
(...)
{
int const result = f2();
if (result) then { (...) }
}
अंतर मामूली लग सकता है, विशेष रूप से इस तरह के एक छोटे से उदाहरण पर। लेकिन एक बड़े कोड आधार पर, यह मदद करेगा: अब कुछ result
मूल्य f1()
को f2()
ब्लॉक करने के लिए परिवहन के लिए कोई जोखिम नहीं है । प्रत्येक result
कड़ाई से अपने दायरे तक सीमित है, अपनी भूमिका को और अधिक सटीक बनाता है। समीक्षक के नजरिए से, यह बहुत अच्छा है, क्योंकि उसके पास चिंता करने और ट्रैक करने के लिए लंबी दूरी के राज्य चर हैं ।
यहां तक कि संकलक बेहतर मदद करेगा: यह मानते हुए कि भविष्य में, कोड के कुछ गलत परिवर्तन के बाद, result
ठीक से प्रारंभ नहीं किया गया है f2()
। दूसरा संस्करण बस काम करने से इनकार कर देगा, संकलन समय पर एक स्पष्ट त्रुटि संदेश (जैसे कि रन समय से बेहतर)। पहले संस्करण में कुछ भी नहीं होगा, के परिणाम को f1()
दूसरी बार परीक्षण किया जाएगा , जिसके परिणाम के लिए भ्रमित किया जा रहा है f2()
।
पूरक जानकारी
ओपन-सोर्स टूल कैपचेक (सी / सी ++ कोड के लिए एक स्थिर विश्लेषण उपकरण) चर के इष्टतम दायरे के बारे में कुछ उत्कृष्ट संकेत प्रदान करता है।
आवंटन पर टिप्पणी के जवाब में: उपरोक्त नियम C में सत्य है, लेकिन कुछ C ++ वर्गों के लिए नहीं हो सकता है।
मानक प्रकार और संरचनाओं के लिए, संकलन समय पर चर के आकार को जाना जाता है। सी में "निर्माण" जैसी कोई चीज नहीं है, इसलिए फ़ंक्शन को कॉल करने पर चर के लिए स्थान केवल स्टैक में आवंटित किया जाएगा (बिना किसी प्रारंभ के)। इसलिए लूप के अंदर वेरिएबल की घोषणा करते समय "शून्य" लागत होती है।
हालाँकि, C ++ क्लासेस के लिए, यह कंस्ट्रक्टर चीज़ है, जिसके बारे में मैं बहुत कम जानता हूँ। मुझे लगता है कि आवंटन संभवत: मुद्दा नहीं होगा, क्योंकि कंपाइलर एक ही स्थान का पुन: उपयोग करने के लिए पर्याप्त चतुर होगा, लेकिन आरंभीकरण प्रत्येक लूप पुनरावृत्ति पर होने की संभावना है।