प्रोग्राम स्टैक से कैसे चर को संग्रहीत और पुनर्प्राप्त किया जाता है?


47

इस प्रश्न के भोलेपन के लिए अग्रिम में माफी। मैं एक 50 वर्षीय कलाकार हूं, जो पहली बार कंप्यूटरों को ठीक से समझने की कोशिश कर रहा है। तो यहाँ जाता है।

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

लेकिन जो मुझे नहीं मिलता है वह यह है कि स्टैक पर वेरिएबल्स को तब एक एप्लिकेशन द्वारा पढ़ा जाता है- अगर मैं घोषणा करता हूं और xपूर्णांक के रूप में असाइन करता हूं x = 3, तो कहते हैं , और स्टोरेज स्टैक पर आरक्षित है और फिर इसका मान 3वहां संग्रहीत किया जाता है, और फिर में उसी फ़ंक्शन को मैं घोषित करता हूं और असाइन yकरता हूं 4, जैसा कहता हूं , और फिर उसके बाद मैं xदूसरी अभिव्यक्ति में उपयोग करता हूं , (कहते हैं z = 5 + x) जब यह नीचे है तो xमूल्यांकन करने के लिए प्रोग्राम कैसे पढ़ zसकता हैyस्टैक पर? मुझे स्पष्ट रूप से कुछ याद आ रहा है। क्या यह है कि स्टैक पर स्थान केवल चर के जीवनकाल / दायरे के बारे में है, और यह कि पूरे स्टैक वास्तव में हर समय कार्यक्रम के लिए सुलभ है? यदि हां, तो क्या इसका मतलब यह है कि कुछ अन्य सूचकांक हैं जो मानों को पुनः प्राप्त करने के लिए स्टैक पर केवल चर के पते रखते हैं? लेकिन फिर मुझे लगा कि स्टैक का पूरा बिंदु यह था कि मान वैरिएबल पते के समान ही संग्रहीत किए गए थे? मेरे दंडित मन में ऐसा लगता है कि अगर यह अन्य सूचकांक है, तो हम एक ढेर की तरह कुछ और बात कर रहे हैं? मैं स्पष्ट रूप से बहुत भ्रमित हूं, और मैं उम्मीद कर रहा हूं कि मेरे सरल प्रश्न का एक सरल उत्तर है।

यहां तक ​​इसे पढ़ने के लिए धन्यवाद।


7
@ fade2black मैं असहमत हूं - उचित लंबाई का उत्तर देना संभव होना चाहिए जो महत्वपूर्ण बिंदुओं को सारांशित करता है।
डेविड रिचीर्बी

9
आप conflating के अत्यंत सामान्य त्रुटि कर रहे हैं मूल्य की तरह साथ जहां यह संग्रहीत किया जाता है । यह कहना केवल असत्य है कि बकल स्टैक पर जाती है। Bools में जाना चर , और चर ढेर पर जाना है, तो अपने जीवनकाल कम हो जाते हैं , और उनके जीवन काल कम होने के लिए नहीं जाना जाता है, तो ढेर पर। यह कैसे C # से संबंधित कुछ विचारों के लिए, blogs.msdn.microsoft.com/ericlippert/2010/09/30/… पर
Eric Lippert

7
इसके अलावा, स्टैक को वैरिएबल में मानों के ढेर के रूप में न समझें । इसे तरीकों के लिए सक्रियण फ़्रेम के ढेर के रूप में सोचें । एक विधि के भीतर आप उस विधि के सक्रियण के किसी भी चर को एक्सेस कर सकते हैं, लेकिन आप कॉलर के चर को एक्सेस नहीं कर सकते, क्योंकि वे उस फ्रेम में नहीं हैं जो स्टैक के शीर्ष पर है
एरिक लिपर्ट

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

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

जवाबों:


24

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

स्टैक इस प्रक्रिया को लागू करने का एक तरीका है - आप इसे एक प्रकार के "फास्ट हीप" के रूप में सोच सकते हैं जिसका आकार सीमित है और इसलिए यह केवल छोटे चर के लिए उपयुक्त है। अतिरिक्त अनुकूलन के रूप में, सभी स्थानीय चर एक ब्लॉक में संग्रहीत किए जाते हैं। चूँकि प्रत्येक स्थानीय चर का आकार ज्ञात होता है, आप ब्लॉक में प्रत्येक चर की भरपाई जानते हैं, और इस तरह से आप इसे एक्सेस करते हैं। यह ढेर पर आवंटित चर के विपरीत है, जिनके पते स्वयं अन्य चर में संग्रहीत हैं।

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

अंत में, मैं उल्लेख करता हूं कि व्यवहार में, कुछ स्थानीय चर रजिस्टरों में संग्रहीत किए जाते हैं। यह तब से है जब रजिस्टरों तक पहुंच स्टैक तक पहुंच की तुलना में तेज है। यह स्थानीय चर के लिए एक स्थान को लागू करने का एक और तरीका है। एक बार फिर, हम ठीक से जानते हैं कि एक चर कहाँ संग्रहीत किया जाता है (इस बार ऑफसेट के माध्यम से नहीं, बल्कि एक रजिस्टर के नाम से), और इस तरह का भंडारण केवल छोटे डेटा के लिए उपयुक्त है।


1
"एक ब्लॉक में आवंटित" एक और कार्यान्वयन विवरण है। हालांकि यह कोई फर्क नहीं पड़ता। कंपाइलर जानता है कि स्थानीय चर के लिए मेमोरी की आवश्यकता कैसे है, यह उस मेमोरी को एक या एक से अधिक ब्लॉकों को आवंटित करता है , और फिर यह उस मेमोरी में स्थानीय चर बनाता है।
एमएसलटर्स

धन्यवाद, सही किया। वास्तव में, इनमें से कुछ "ब्लॉक" सिर्फ रजिस्टर हैं।
युवल फिल्मस

1
आपको केवल रिटर्न पते को स्टोर करने के लिए स्टैक की आवश्यकता है, यदि ऐसा है। आप ढेर पर आसानी से स्टैक के बिना पुनरावृत्ति को लागू कर सकते हैं, एक सूचक को ढेर पर वापसी पते पर पास करके।
युवल फिल्मस

1
@MikeCaron स्टैक का पुनरावर्तन से कोई लेना देना नहीं है। आप अन्य कार्यान्वयन रणनीतियों में "चर दूर क्यों उड़ाएंगे"?
बाग़ जू

1
@gardenhead सबसे स्पष्ट विकल्प (और जो वास्तव में / का उपयोग किया गया था) प्रत्येक प्रक्रिया के चर को सांख्यिकीय रूप से आवंटित करना है। तेज, सरल, पूर्वानुमेय ... लेकिन कोई पुनरावृत्ति या पुनर्संयोजन की अनुमति नहीं है। वह और पारंपरिक स्टैक पाठ्यक्रम के एकमात्र विकल्प नहीं हैं (गतिशील रूप से सब कुछ आवंटित करना एक और चीज है), लेकिन वे आम तौर पर चर्चा करते हैं जब स्टैक को औचित्य दिया जाता है :)
hobbs

23

yस्टैक पर होने xसे शारीरिक रूप से एक्सेस होने से नहीं रोका जा सकता है, जैसा कि आपने बताया, कंप्यूटर स्टैक को अन्य स्टैक से अलग बनाता है।

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

क्या यह है कि स्टैक पर स्थान केवल चर के जीवनकाल / दायरे के बारे में है, और यह कि पूरे स्टैक वास्तव में हर समय कार्यक्रम के लिए सुलभ है?

हाँ। जब आप किसी फ़ंक्शन से बाहर निकलते हैं, तो स्टैक पॉइंटर अपने पिछले स्थान पर वापस चला जाता है, प्रभावी रूप से मिटा रहा है xऔर y, लेकिन तकनीकी रूप से वे तब भी रहेंगे जब तक कि मेमोरी का उपयोग किसी और चीज के लिए नहीं किया जाता है। इसके अतिरिक्त, यदि आपका फ़ंक्शन किसी अन्य फ़ंक्शन को कॉल करता है, xऔर yअभी भी वहां होगा और स्टैक में जानबूझकर बहुत नीचे तक पहुँचा जा सकता है।


1
ऐसा लगता है कि अब तक ओपी मेज पर लाता है कि पृष्ठभूमि ज्ञान से परे बात नहीं करने के संदर्भ में सबसे साफ जवाब है। ओपी को लक्षित करने के लिए +1!
बेन I.

1
मैं भी सहमत हूँ! हालाँकि सभी उत्तर बेहद मददगार हैं और मैं बहुत आभारी हूं, मेरी मूल पोस्ट प्रेरित थी क्योंकि मुझे लगता है कि (डी) यह पूरी तरह से स्टैक / ढेर बात समझने के लिए बिल्कुल मौलिक है कि मूल्य / संदर्भ प्रकार का अंतर कैसे उत्पन्न होता है, लेकिन मैं नहीं कर सका टी देखें कि कैसे आप केवल "स्टैक" के शीर्ष को देख सकते हैं। तो आपका जवाब मुझे इससे मुक्त करता है। (मुझे वही एहसास मिलता है जो मुझे तब मिला था जब मैंने पहली बार भौतिकी के सभी विभिन्न व्युत्क्रम वर्ग कानूनों को महसूस किया था कि बस एक गोले से निकलने वाले विकिरण की ज्यामिति से बाहर हो जाते हैं, और आप इसे देखने के लिए एक सरल आरेख बना सकते हैं।)
सेलीन एटवुड

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

1
@CelineAtwood कृपया ध्यान दें कि स्टैक से हटाए जाने के बाद "बल द्वारा" चर तक पहुंचने की कोशिश करने से आपको अप्रत्याशित / अपरिभाषित व्यवहार मिलेगा और ऐसा नहीं किया जाना चाहिए। ध्यान दें, मैंने यह नहीं कहा कि कुछ भाषाओं में आप इसे आज़माने की अनुमति नहीं देंगे। फिर भी, यह एक प्रोग्रामिंग गलती होगी और इसे टाला जाना चाहिए।
code_dredd

12

एक संकलक स्टैक का प्रबंधन कैसे करता है और स्टैक पर मान कैसे एक्सेस किए जाते हैं, इसका एक ठोस उदाहरण प्रदान करने के लिए, हम विज़ुअल चित्रणों को देख सकते हैं, साथ ही GCCलिनक्स वातावरण में i386 के साथ लक्ष्य आर्किटेक्चर के रूप में उत्पन्न कोड ।

1. स्टैक फ्रेम

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

CSAPP स्टैक फ़्रेम

2. स्टैक फ्रेम प्रबंधन और चर स्थान

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

बेस पॉइंटर, ebpकन्वेंशन द्वारा, स्टैक के नीचे, या बेस का मेमोरी एड्रेस होता है। स्टैक फ्रेम के भीतर सभी मानों की स्थिति की गणना एक संदर्भ के रूप में आधार पॉइंटर में पते का उपयोग करके की जा सकती है। यह ऊपर चित्र में दर्शाया गया है: %ebp + 4उदाहरण के लिए, बेस पॉइंटर प्लस 4 में संग्रहीत मेमोरी एड्रेस है।

3. कंपाइलर-जनरेटेड कोड

लेकिन मुझे जो नहीं मिलता है वह यह है कि स्टैक पर वेरिएबल्स को एक एप्लिकेशन द्वारा कैसे पढ़ा जाता है- अगर मैं x को पूर्णांक के रूप में घोषित करता हूं और असाइन करता हूं, तो x = 3 कहते हैं, और स्टैक पर संग्रहण आरक्षित होता है और फिर इसका मान 3 संग्रहीत किया जाता है वहाँ, और फिर उसी फ़ंक्शन में मैं y घोषित करता हूं और असाइन करता हूं, 4 कहता हूं, और फिर उसके बाद मैं x का उपयोग किसी अन्य अभिव्यक्ति में करता हूं, (z = 5 + x) कहूं कि जब z का मूल्यांकन करने के लिए प्रोग्राम x को कैसे पढ़ सकता है? यह स्टैक पर y के नीचे है?

आइए C में लिखे गए एक सरल उदाहरण कार्यक्रम का उपयोग करें कि यह कैसे काम करता है:

int main(void)
{
        int x = 3;
        int y = 4;
        int z = 5 + x;

        return 0;
}

आइए इस C स्रोत पाठ के लिए GCC द्वारा निर्मित असेंबली पाठ की जांच करें (मैंने स्पष्टता के लिए इसे थोड़ा साफ किया:)

main:
    pushl   %ebp              # save previous frame's base address on stack
    movl    %esp, %ebp        # use current address of stack pointer as new frame base address
    subl    $16, %esp         # allocate 16 bytes of space on stack for function data
    movl    $3, -12(%ebp)     # variable x at address %ebp - 12
    movl    $4, -8(%ebp)      # variable y at address %ebp - 8
    movl    -12(%ebp), %eax   # write x to register %eax
    addl    $5, %eax          # x + 5 = 9
    movl    %eax, -4(%ebp)    # write 9 to address %ebp - 4 - this is z
    movl    $0, %eax
    leave

हम जो निरीक्षण करते हैं, वह यह है कि चर x, y और z क्रमशः %ebp - 12, %ebp -8और पते पर स्थित %ebp - 4हैं। दूसरे शब्दों में, सीपीयू रजिस्टर में सहेजे गए मेमोरी एड्रेस का उपयोग करके स्टैक फ्रेम के भीतर चर के स्थानों की main()गणना की जाती है %ebp

4. स्टैक पॉइंटर से परे मेमोरी में डेटा दायरे से बाहर है

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

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

जैसा कि फ़ंक्शन कहा जाता है और वापस लौटता है, स्टैक पॉइंटर को घटाया और बढ़ाया जाता है। के बाद यह क्षेत्र से बाहर है ढेर के लिए लिखा डाटा गायब नहीं है, लेकिन संकलक इस डेटा को संदर्भित निर्देश उत्पन्न नहीं करता है संकलक का उपयोग कर इन आंकड़ों के पतों की गणना करने के लिए कोई रास्ता नहीं है क्योंकि वहाँ %ebpया %esp

5. सारांश

कोड जिसे सीधे सीपीयू द्वारा निष्पादित किया जा सकता है, संकलक द्वारा उत्पन्न किया जाता है। कंपाइलर फ़ंक्शन और CPU रजिस्टरों के लिए स्टैक, स्टैक फ़्रेम का प्रबंधन करता है। I386 आर्किटेक्चर पर निष्पादित करने के उद्देश्य से कोड में स्टैक फ्रेम में चर के स्थानों को ट्रैक करने के लिए जीसीसी द्वारा उपयोग की जाने वाली एक रणनीति स्टैक फ्रेम बेस पॉइंटर में मेमोरी एड्रेस का उपयोग करना है, स्टैक फ्रेम %ebpमें स्थानों के लिए चर के संदर्भ और वैल्यू मान लिखें। पते के लिए ऑफ़सेट में %ebp


मेरा अगर मैं पूछूं कि वह छवि कहां से आई? यह संदिग्ध रूप से परिचित लग रहा है ... :-) यह एक अतीत की पाठ्यपुस्तक में रहा होगा।
द ग्रेट डक

1
nvmd। मैंने सिर्फ लिंक देखा। यह मैंने सोचा था। उस पुस्तक को साझा करने के लिए +1।
द ग्रेट डक

1
Gcc

9

दो विशेष रजिस्टर हैं: ईएसपी (स्टैक पॉइंटर) और ईबीपी (बेस पॉइंटर)। जब किसी प्रक्रिया को लागू किया जाता है तो पहले दो ऑपरेशन आमतौर पर होते हैं

push        ebp  
mov         ebp,esp 

पहला ऑपरेशन स्टैक पर ईबीपी के मूल्य को बचाता है, और दूसरा ऑपरेशन स्टैक पॉइंटर के मान को बेस पॉइंटर (स्थानीय चर तक पहुंचने के लिए) में लोड करता है। इसलिए, ईबीपी उसी स्थान की ओर इशारा करता है, जैसा ईएसपी करता है।

असेंबलर ने चर नामों को EBP ऑफसेट में अनुवाद किया। उदाहरण के लिए, यदि आपके पास दो स्थानीय चर हैं x,y, और आपके पास कुछ ऐसा है

  x = 1;
  y = 2;
  return x + y;

तब इसका अनुवाद कुछ इस तरह किया जा सकता है

   push        ebp  
   mov         ebp,esp
   mov  DWORD PTR [ ebp + 6],  1   ;x = 1
   mov  DWORD PTR [ ebp + 14], 2   ;y = 2
   mov  eax, [ ebp + 6 ]
   add  [ ebp + 14 ], eax          ; x + y 
   mov  eax, [ ebp + 14 ] 
   ...  

संकलन समय पर 6 और 14 को ऑफसेट मानों की गणना की जाती है।

यह मोटे तौर पर यह कैसे काम करता है। विवरण के लिए एक संकलक पुस्तक देखें।


14
यह इंटेल x86 के लिए विशिष्ट है। एआरएम पर, रजिस्टर एसपी (आर 13) का उपयोग किया जाता है, साथ ही एफपी (आर 11)। और x86 पर, रजिस्टरों की कमी का मतलब है कि आक्रामक संकलक ईबीपी का उपयोग नहीं करेंगे क्योंकि यह ईएसपी से प्राप्त किया जा सकता है। यह स्पष्ट है कि वह अंतिम उदाहरण है, जहां सभी ईबीपी-सापेक्ष पते को ईएसपी-सापेक्ष में अनुवाद किया जा सकता है, जिसमें किसी अन्य परिवर्तन की आवश्यकता नहीं है।
एमएसलटर्स

क्या आप पहले स्थान पर x, y के लिए जगह बनाने के लिए ESP पर SUB याद नहीं कर रहे हैं?
हेगन वॉन एटिजन

@ हेगनवोनएटजेन, शायद। मैं सिर्फ यह विचार व्यक्त करना चाहता था कि स्टैक पर आवंटित चर को हार्डवेयर रजिस्टरों का उपयोग करके कैसे एक्सेस किया जाता है।
fade2black

नीचे की ओर, टिप्पणी कृपया !!!
fade2black

8

आप भ्रमित हैं क्योंकि स्टैक में संग्रहीत स्थानीय चर स्टैक के एक्सेस नियम के साथ एक्सेस नहीं किए जाते हैं: फ़र्स्ट इन लास्ट आउट, या सिर्फ फ़िलो

बात यह है कि फिल्मा नियम स्थानीय चर के बजाय कॉल सीक्वेंस और स्टैक फ्रेम को कार्य करने के लिए लागू होता है ।

स्टैक फ्रेम क्या है?

जब आप किसी फ़ंक्शन में प्रवेश करते हैं, तो इसे स्टैक पर कुछ मात्रा में मेमोरी दी जाती है, जिसे स्टैक फ्रेम कहा जाता है। फ़ंक्शन के स्थानीय चर स्टैक फ़्रेम में संग्रहीत किए जाते हैं। आप सोच सकते हैं कि स्टैक फ्रेम का आकार फ़ंक्शन से फ़ंक्शन में भिन्न होता है क्योंकि प्रत्येक फ़ंक्शन के पास विभिन्न चर और आकार के स्थानीय चर होते हैं।

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

तो यह फिल्म व्यवहार कब दिखाई देता है?

अब, यदि आप किसी अन्य फ़ंक्शन को कॉल करते हैं, तो क्या होता है? कैली फंक्शन का अपना स्टैक फ्रेम होना चाहिए, और यह स्टैक फ्रेम है जिसे स्टैक में धकेल दिया जाता है । यही है, कॉलरी फ़ंक्शन के स्टैक फ़्रेम को कॉलर फ़ंक्शन के स्टैक फ़्रेम के शीर्ष पर रखा गया है। और अगर यह कैलली फ़ंक्शन किसी अन्य फ़ंक्शन को कॉल करता है, तो इसके स्टैक फ्रेम को फिर से, स्टैक के शीर्ष पर धकेल दिया जाएगा।

यदि कोई फ़ंक्शन वापस आता है तो क्या होता है? जब फोन करने वाले कार्य करने के लिए एक कॉल प्राप्त करने वाला समारोह रिटर्न, कॉल प्राप्त करने वाला समारोह के ढेर फ्रेम है पॉप ढेर से बाहर, भविष्य में उपयोग के लिए अंतरिक्ष मुक्त हो गया।

तो आपके सवाल से:

क्या यह है कि स्टैक पर स्थान केवल चर के जीवनकाल / दायरे के बारे में है, और यह कि पूरे स्टैक वास्तव में हर समय कार्यक्रम के लिए सुलभ है?

आप यहां काफी सही हैं क्योंकि फ़ंक्शन के वापस आने पर स्टैक फ्रेम पर स्थानीय चर मान वास्तव में मिटाए नहीं जाते हैं। मान बस वहाँ रहता है, हालाँकि मेमोरी स्थान जहाँ इसे संग्रहीत किया जाता है, किसी भी फ़ंक्शन के स्टैक फ्रेम से संबंधित नहीं है। मान तब मिटाया जाता है जब कोई अन्य फ़ंक्शन अपने स्टैक फ़्रेम को प्राप्त करता है जिसमें स्थान शामिल होता है और उस स्मृति स्थान पर कुछ अन्य मान लिखता है।

फिर ढेर से क्या स्टैंसिलेंसेट्स?

स्टैक और हीप इस अर्थ में समान हैं कि वे दोनों ऐसे नाम हैं जो स्मृति पर कुछ स्थान का उल्लेख करते हैं। चूँकि हम इसके पते के साथ मेमोरी पर किसी भी स्थान तक पहुँच सकते हैं, आप स्टैक या ढेर में किसी भी स्थान तक पहुँच सकते हैं।

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


4

भाषा रनटाइम सिस्टम द्वारा स्थानीय चर को लागू करने के कई तरीके हैं। स्टैक का उपयोग करना एक सामान्य कुशल समाधान है, जिसका उपयोग कई व्यावहारिक मामलों में किया जाता है।

सहज रूप से, एक स्टैक पॉइंटर spको रनटाइम के दौरान (एक निश्चित पते पर, या एक रजिस्टर में - यह वास्तव में कोई फर्क नहीं पड़ता) रखा जाता है। मान लें कि हर "पुश" स्टैक पॉइंटर को बढ़ाता है।

संकलित समय पर, कंपाइलर प्रत्येक चर का पता निर्धारित करता है जैसे sp - Kकि Kएक स्थिरांक है जो केवल चर के दायरे पर निर्भर करता है (इसलिए संकलन समय पर गणना की जा सकती है)।

ध्यान दें कि हम "स्टैक" शब्द का उपयोग यहां ढीले अर्थों में कर रहे हैं। यह स्टैक केवल पुश / पॉप / टॉप ऑपरेशंस के माध्यम से एक्सेस नहीं किया जाता है, बल्कि इसका उपयोग करके भी एक्सेस किया जाता है sp - K

उदाहरण के लिए इस सूडोकोड पर विचार करें:

procedure f(int x, int y) {
  print(x,y);    // (1)
  if (...) {
    int z=x+y; // (2)
    print(x,y,z);  // (3)
  }
  print(x,y); // (4)
  return;
}

जब प्रक्रिया को बुलाया जाता है, x,yतो स्टैक पर तर्क पारित किए जा सकते हैं। सादगी के लिए, मान लें कि सम्मेलन xपहले कॉलर को धक्का देता है , फिर y

फिर, बिंदु (1) पर संकलक xपर sp - 2और yपर पा सकते हैं sp - 1

बिंदु (2) पर, एक नया चर दायरे में लाया जाता है। संकलक कि रकम कोड उत्पन्न करता है x+y, यानी क्या द्वारा बताया गया है sp - 2और sp - 1, और ढेर पर योग का परिणाम धक्का।

बिंदु (3) पर, zमुद्रित किया जाता है। संकलक जानता है कि यह दायरे में अंतिम चर है, इसलिए इसे इसके द्वारा इंगित किया गया है sp - 1। यह अब नहीं है y, क्योंकि spबदल गया है। फिर भी, yसंकलक को प्रिंट करने के लिए पता है कि यह इस दायरे में, इसे पा सकता है sp - 2। इसी तरह, xअब पाया जाता है sp - 3

बिंदु (4) पर, हम दायरे से बाहर निकलते हैं। zआबाद है, और yफिर से पते पर मिला है sp - 1, और xहै sp - 2

जब हम वापस लौटते हैं, या तो fया कॉलर x,yस्टैक से पॉप करता है।

तो, Kसंकलक के लिए गणना करना गिनती का विषय है कि कितने चर दायरे में हैं, मोटे तौर पर। वास्तविक दुनिया में, यह वास्तव में अधिक जटिल है क्योंकि सभी चरों का आकार समान नहीं होता है, इसलिए इसकी गणना Kथोड़ी अधिक जटिल होती है। कभी-कभी, स्टैक में भी रिटर्न एड्रेस होता है f, इसलिए Kइसे "स्किप ओवर" भी करना चाहिए। लेकिन ये तकनीकी हैं।

ध्यान दें कि, कुछ प्रोग्रामिंग भाषाओं में, चीजें और भी जटिल हो सकती हैं यदि अधिक जटिल विशेषताओं को संभालना चाहिए। उदाहरण के लिए नेस्टेड प्रक्रियाओं के लिए बहुत सावधानीपूर्वक विश्लेषण की आवश्यकता होती है, क्योंकि Kअब कई रिटर्न पतों को "छोड़ना" पड़ता है, खासकर अगर नेस्टेड प्रक्रिया पुनरावर्ती है। क्लोजर / लैम्ब्डा / अनाम फ़ंक्शन को "कैप्चर किए गए" चर को संभालने के लिए कुछ देखभाल की भी आवश्यकता होती है। फिर भी, उपरोक्त उदाहरण को मूल विचार को चित्रित करना चाहिए।


3

सबसे आसान विचार यह है कि चर के रूप में स्मृति में पतों के लिए नाम तय करें । वास्तव में, कुछ कोडांतरक मशीन कोड को इस तरह से प्रदर्शित करते हैं ("पता में स्टोर मूल्य 5 i", जहां iएक चर नाम है)।

इनमें से कुछ पते "निरपेक्ष" हैं, जैसे वैश्विक चर, कुछ "सापेक्ष" हैं, जैसे स्थानीय चर। फ़ंक्शंस में चर (यानी पते) "स्टैक" पर कुछ जगह के सापेक्ष होते हैं जो हर फ़ंक्शन के आह्वान के लिए अलग-अलग होते हैं; उसी तरह एक ही नाम विभिन्न वास्तविक वस्तुओं को संदर्भित कर सकता है, और एक ही फ़ंक्शन के लिए परिपत्र कॉल स्वतंत्र मेमोरी पर काम करने वाले स्वतंत्र चालान हैं।


2

डेटा आइटम जो स्टैक पर जा सकते हैं, उन्हें स्टैक पर रखा जाता है - हाँ! यह एक प्रीमियम स्पेस है। इसके अलावा, एक बार जब हम xस्टैक में धकेल दिए जाते हैं और फिर स्टैक में धकेल yदिए जाते हैं, तो आदर्श रूप से हम xतब तक एक्सेस नहीं कर सकते हैं जब तक yकि वहाँ नहीं है। हमें पॉप करने की जरूरत हैy करनेx । आपने उन्हें सही किया।

स्टैक चर का नहीं है, लेकिन का है frames

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

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

उत्तर का अंत। इन तख्ते पर अधिक (शेख़ी)

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

ध्यान दें कि स्टैक-फ्रेम की संरचना और संरचना प्रोग्रामिंग भाषा से प्रोग्रामिंग भाषा तक भिन्न होती है। यहां तक ​​कि एक भाषा के भीतर भी विभिन्न कार्यान्वयनों में सूक्ष्म अंतर हो सकते हैं।


CS पर विचार करने के लिए धन्यवाद। मैं अब एक प्रोग्रामर हूं जो पियानो सबक ले रहा है :)

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