स्कोप
चर { }
की एक जोड़ी के दायरे के अंदर घोषित ढेर पर हैं। यह किसी फ़ंक्शन की शुरुआत में घोषित चर या फ़ंक्शन के भीतर { }
की किसी भी जोड़ी में लागू होता है।
int myfunc()
{
int i = 0; // On the stack, scoped: myfunc
printf("%i\n");
if (1)
{
int j = 1; // On the stack, scope: this if statement
printf("%i %i\n",i,j);
}
printf("%i %i\n",i,j); // Won't work, no j
}
इन दिनों चर के दायरे आसपास { }
तक सीमित है। मुझे याद है कि कुछ पुराने माइक्रोसॉफ्ट कंपाइलर्स ने दायरे को सीमित नहीं किया है, और अंतिम printf()
के ऊपर उदाहरण में संकलन होगा।
तो यह स्मृति में कहां है?
i
और j
की स्मृति केवल ढेर पर आरक्षित है। यह malloc()
के साथ मेमोरी आवंटन के समान नहीं है। यह महत्वपूर्ण है, क्योंकि तुलना में malloc()
कॉल करना बहुत धीमा है। malloc()
का उपयोग करके गतिशील रूप से आवंटित स्मृति के साथ आपको free()
पर कॉल करना होगा।
असल में संकलक समय से पहले जानता है कि किसी फ़ंक्शन के चर के लिए किस स्थान की आवश्यकता है और जो कोड स्टैक पॉइंटर होता है उसके संबंध में स्मृति को संदर्भित करता है जब myfunc()
कहा जाता है। जब तक ढेर काफी बड़ा होता है (सामान्यतः 2 एमबीइट्स, ओएस पर निर्भर करता है), सब अच्छा है।
ढेर अतिप्रवाह स्थिति है जहाँ myfunc()
ढेर सूचक पहले से ही ढेर के अंत के करीब के साथ कहा जाता है में होता है (यानी myfunc()
एक समारोह है जो बदले में किसी अन्य के द्वारा बुलाया गया था जो इसे स्वयं अभी तक एक से बुलाया गया था द्वारा कहा जाता है , आदि कार्यों के लिए नेस्टेड कॉल की प्रत्येक परत स्टैक पॉइंटर को थोड़ा और आगे ले जाती है, और जब फ़ंक्शन वापस आती है तो केवल वापस ले जाती है)।
तो ढेर सूचक और ढेर के अंत के बीच की जगह काफी बड़ा सभी चर कि myfunc()
घोषित किये गए हैं पकड़ करने के लिए नहीं है, myfunc()
के लिए कोड बस ढेर के अंत से परे स्थानों का उपयोग करने की कोशिश करेंगे। यह लगभग हमेशा एक बुरी चीज है, और वास्तव में कितना बुरा है और यह ध्यान रखना कितना मुश्किल है कि कुछ गलत हो गया है ऑपरेटिंग सिस्टम पर निर्भर करता है। छोटे एम्बेडेड माइक्रो नियंत्रकों पर यह एक दुःस्वप्न हो सकता है क्योंकि इसका आमतौर पर प्रोग्राम के डेटा (जैसे वैश्विक चर) के कुछ अन्य हिस्से को चुपचाप ओवरराइट किया जाता है, और इसे डीबग करना बहुत मुश्किल हो सकता है। बड़े सिस्टम (लिनक्स, विंडोज) पर ओएस आपको बताएगा कि क्या हुआ है, या केवल स्टैक को बड़ा कर देगा।
रनटाइम क्षमता संबंधी
उपरोक्त उदाहरण मैं i
और j
को मान निर्दिष्ट कर रहा हूँ में। यह वास्तव में रनटाइम की एक छोटी राशि लेता है। j
को केवल कथन और बाद की शाखा के मूल्यांकन के बाद 1 को सौंपा गया है जहां j
घोषित किया गया है।
उदाहरण के लिए कहें कि अगर कथन का मूल्यांकन सत्य के रूप में नहीं किया गया था; उस स्थिति में j
कभी सौंपा गया नहीं है। यदि j
को myfunc()
की शुरुआत में घोषित किया गया था तो यह हमेशा 1 का मान सौंपा जाएगा चाहे यह बयान सत्य था - समय की मामूली बर्बादी। लेकिन एक कम मामूली उदाहरण पर विचार करें जहां एक बड़ी सरणी शुरू की गई है; जो अधिक निष्पादन समय लेगा।
int myfunc()
{
int i = 0; // On the stack, scoped: myfunc
int k[10000] = {0} // On the stack, scoped: myfunc. A complete waste of time
// when the if statement evaluates to false.
printf("%i\n");
if (0)
{
int j = 1; // On the stack, scope: this if statement
// It would be better to move the declaration of k to here
// so that it is initialised only when the if evaluates to true.
printf("%i %i %i\n",i,j,k[500]);
}
printf("%i %i\n",i,j); // Won't work, no j
}
myfunc()
के शीर्ष पर k
की घोषणा रखने का मतलब है कि एक पाश 10,000 लंबे हर बार myfunc()
कहा जाता है k आरंभ करने के लिए मार डाला जाता है। हालांकि यह कभी भी उपयोग नहीं किया जाता है, ताकि लूप समय की पूरी बर्बादी हो।
बेशक
, इन तुच्छ उदाहरण में compilers अनावश्यक कोड, आदि बाहर अनुकूलित करेंगे वास्तविक कोड में जहां संकलक समय से आगे अनुमान नहीं लगा सकते क्या निष्पादन प्रवाह तो हो जाएगा बातें जगह में छोड़ दिया जाता है।
क्यों आपके कंपाइलर को खोजने के लिए उत्पन्न कोड को न देखें? –
@CarlNorum शायद क्योंकि सभी को असेंबलर नहीं पता? – Kolyunya
इस मामले में यह वास्तव में एक अच्छा जवाब नहीं है। अगर आप जानना चाहते हैं कि क्या हो रहा है तो आपको सीखना होगा। यहां व्यवहार को भाषा मानक के आधार पर अनुमान लगाकर निर्धारित नहीं किया जा सकता है, क्योंकि भाषा इसे पर्याप्त रूप से निर्दिष्ट नहीं करती है।कम से कम, एबीआई और/या प्रक्रिया कॉल मानक को तोड़ना जरूरी है। –