मैं समझता हूं कि स्टैक पॉइंटर क्या है - लेकिन इसका उपयोग किस लिए किया जाता है?


11

स्टैक पॉइंटर स्टैक के शीर्ष पर इंगित करता है, जो डेटा को "LIFO" के आधार पर संग्रहीत करता है। किसी और की उपमा को चुराने के लिए, यह उन व्यंजनों के ढेर की तरह है जिसमें आप शीर्ष पर व्यंजन लेते हैं और लेते हैं। स्टैक पॉइंटर, OTOH, स्टैक के शीर्ष "डिश" को इंगित करता है। कम से कम, यह x86 के लिए सच है।

लेकिन कंप्यूटर / प्रोग्राम "देखभाल" क्यों करता है जो स्टैक पॉइंटर की ओर इशारा करता है? दूसरे शब्दों में, स्टैक पॉइंटर का क्या उद्देश्य है और यह जानने के लिए कि यह सेवा करने के लिए कहाँ इंगित करता है?

सी प्रोग्रामर द्वारा समझे जाने वाले स्पष्टीकरण की सराहना की जाएगी।


क्योंकि आप राम के ढेर के शीर्ष को नहीं देख सकते हैं, जैसे आप व्यंजनों के ढेर के ऊपर देख सकते हैं।
tkausl


8
आप एक ढेर के नीचे से एक डिश नहीं लेते हैं। आप शीर्ष पर एक जोड़ते हैं और कोई अन्य इसे ऊपर से लेता है । आप यहाँ एक कतार के बारे में सोच रहे हैं।
किलन फ़ॉथ

@Snowman आपका संपादन प्रश्न का अर्थ बदलने लगता है। Moonman239, क्या आप यह सत्यापित कर सकते हैं कि स्नोमैन का परिवर्तन सटीक है, विशेष रूप से इसके अलावा "इस स्टैक का वास्तव में क्या उद्देश्य है, इसकी संरचना को समझाने के विपरीत?"
8bittree

1
@ 8bittree कृपया संपादन विवरण देखें: मैंने प्रश्न को शरीर की विषय पंक्ति में बताए अनुसार कॉपी किया था। बेशक, मैं हमेशा इस संभावना के लिए खुला रहता हूं कि मैंने कुछ बदल दिया है और मूल लेखक हमेशा वापस रोल करने या अन्यथा पोस्ट को संपादित करने के लिए स्वतंत्र है।

जवाबों:


23

इसकी संरचना को समझाने के विपरीत, यह स्टैक वास्तव में किस उद्देश्य से काम करता है?

आपके पास कई उत्तर हैं जो स्टैक पर संग्रहीत डेटा की संरचना का सटीक वर्णन करते हैं, जो मैं ध्यान देता हूं कि आपके द्वारा पूछे गए प्रश्न के विपरीत है।

स्टैक जो कार्य करता है वह उद्देश्य है: स्टैक बिना कोरटाइन के किसी भाषा में जारी रहने के संशोधन का हिस्सा है

चलो कि अनपैक करें।

निरंतरता बस डाल दी जाती है, सवाल का जवाब "मेरे कार्यक्रम में आगे क्या होने जा रहा है?" हर कार्यक्रम में हर बिंदु पर आगे कुछ होने वाला है। दो ऑपरेंड की गणना होने जा रही है, फिर कार्यक्रम उनकी राशि की गणना करके जारी है, और फिर एक चर को योग असाइन करके कार्यक्रम जारी है, और फिर ... और इसी तरह।

अमूर्त अवधारणा का एक ठोस कार्यान्वयन करने के लिए पुनरीक्षण केवल एक उच्च शब्द है। "आगे क्या होगा?" एक अमूर्त अवधारणा है; जिस तरह से स्टैक बिछाया जाता है वह इस बात का एक हिस्सा है कि कैसे अमूर्त अवधारणा को वास्तविक मशीन में बदल दिया जाता है जो वास्तव में चीजों की गणना करता है।

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

एक स्टैक निरंतरता को कैसे लागू करता है? अन्य उत्तर कहते हैं कि कैसे। वैरिएबल और टेम्पोररी के स्टैक स्टोर (1) मान जिनके जीवनकाल को वर्तमान विधि की सक्रियता से अधिक नहीं जाना जाता है, और (2) सबसे हालिया विधि सक्रियण से जुड़े निरंतरता कोड का पता है। स्टैक को संभालने वाली अपवाद वाली भाषाओं में "त्रुटि निरंतरता" के बारे में जानकारी भी संग्रहीत की जा सकती है - अर्थात, जब कोई असाधारण कार्य होता है तो कार्यक्रम आगे क्या करेगा।

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

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


ध्यान दें कि आपके द्वारा उद्धृत किया गया उद्धरण गलती से किसी अन्य उपयोगकर्ता द्वारा संपादित किया गया था और तब से इसे ठीक कर दिया गया है, जिससे यह उत्तर प्रश्न का पता नहीं लगा सकता है।
8bittree

2
मुझे स्पष्ट है कि स्पष्टता बढ़ाने के लिए एक स्पष्टीकरण चाहिए । मैं पूरी तरह से आश्वस्त नहीं हूं कि "स्टैक बिना

4

स्टैक का सबसे बुनियादी उपयोग कार्यों के लिए रिटर्न एड्रेस स्टोर करना है:

void a(){
    sub();
}
void b(){
    sub();
}
void sub() {
    //should i got back to a() or to b()?
}

और C के दृष्टिकोण से यह सब है। संकलक बिंदु से:

  • सभी फ़ंक्शन तर्क सीपीयू रजिस्टरों द्वारा पारित किए जाते हैं - यदि पर्याप्त रजिस्टर नहीं हैं तो तर्कों को स्टैक पर रखा जाएगा
  • फ़ंक्शन समाप्त होने के बाद (अधिकांश) रजिस्टरों में प्रवेश करने से पहले समान मान होना चाहिए - इसलिए उपयोग किए गए रजिस्टरों को स्टैक पर बैकअप किया जाता है

और ओएस के दृष्टिकोण से: प्रोग्राम को किसी भी समय बाधित किया जा सकता है इसलिए जब हम सिस्टम कार्य के साथ हमें सीपीयू राज्य को पुनर्स्थापित करना होता है, तो स्टैक पर सब कुछ स्टोर करने देता है

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


1
मुझे लगता है कि यह कहना अधिक सटीक है कि तर्कों को ढेर पर धकेल दिया जाता है, हालांकि अक्सर एक अनुकूलन रजिस्टर का उपयोग प्रोसेसर पर करने के बजाय किया जाता है जिसमें कार्य के लिए पर्याप्त मुक्त रजिस्टर होते हैं। यह एक नट है, लेकिन मुझे लगता है कि यह बेहतर तरीके से मेल खाता है कि भाषाएं ऐतिहासिक रूप से कैसे विकसित हुई हैं। जल्द से जल्द C / C ++ कंपाइलरों ने इसके लिए रजिस्टरों का उपयोग नहीं किया।
रोबोट

4

LIFO बनाम FIFO

LIFO का मतलब लास्ट इन, फर्स्ट आउट है। जैसा कि, स्टैक में रखा अंतिम आइटम स्टैक से निकाला गया पहला आइटम है।

आपने अपने व्यंजनों की उपमा ( पहले संशोधन में ) के साथ जो वर्णन किया है , वह एक कतार या फीफो, फर्स्ट इन, फर्स्ट आउट है।

दोनों के बीच प्रमुख अंतर यह है कि LIFO / स्टैक एक ही छोर से (आवेषण) और चबूतरे (हटाता है), और एक FIFO / कतार विपरीत छोरों से ऐसा करता है।

// Both:

Push(a)
-> [a]
Push(b)
-> [a, b]
Push(c)
-> [a, b, c]

// Stack            // Queue
Pop()               Pop()
-> [a, b]           -> [b, c]

ढेर सूचक

आइए एक नज़र डालते हैं कि स्टैक के हुड के नीचे क्या हो रहा है। यहाँ कुछ मेमोरी है, प्रत्येक बॉक्स एक पता है:

...[ ][ ][ ][ ]...                       char* sp;
    ^- Stack Pointer (SP)

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

तो चलो a, b, and cफिर से धक्का । बाईं ओर ग्राफिक्स, मध्य में "उच्च स्तर" ऑपरेशन, दाईं ओर C-ish छद्म कोड:

...[a][ ][ ][ ]...        Push('a')      *sp = 'a';
    ^- SP
...[a][ ][ ][ ]...                       ++sp;
       ^- SP

...[a][b][ ][ ]...        Push('b')      *sp = 'b';
       ^- SP
...[a][b][ ][ ]...                       ++sp;
          ^- SP

...[a][b][c][ ]...        Push('c')      *sp = 'c';
          ^- SP
...[a][b][c][ ]...                       ++sp;
             ^- SP

जैसा कि आप देख सकते हैं, हर बार जब हम pushउस स्थान में तर्क डालते हैं, जो स्टैक पॉइंटर वर्तमान में इंगित कर रहा है, और अगले स्थान पर इंगित करने के लिए स्टैक पॉइंटर को समायोजित करता है।

अब चलो पॉप:

...[a][b][c][ ]...        Pop()          --sp;
          ^- SP
...[a][b][c][ ]...                       return *sp; // returns 'c'
          ^- SP
...[a][b][c][ ]...        Pop()          --sp;
       ^- SP
...[a][b][c][ ]...                       return *sp; // returns 'b'
       ^- SP

Popके विपरीत है push, यह पिछले स्थान पर इंगित करने के लिए स्टैक पॉइंटर को समायोजित करता है और उस आइटम को हटाता है जो वहां था (आमतौर पर इसे जिसे वापस बुलाया जाता है pop)।

आपने शायद गौर किया bऔर cअभी भी याद में हैं। मैं आपको केवल यह आश्वासन देना चाहता हूं कि वे टाइपोस नहीं हैं। हम जल्द ही उस पर लौटेंगे।

स्टैक पॉइंटर के बिना जीवन

आइए देखें कि क्या होता है अगर हमारे पास स्टैक पॉइंटर नहीं है। फिर से धक्का देने के साथ शुरू:

...[ ][ ][ ][ ]...
...[ ][ ][ ][ ]...        Push(a)        ? = 'a';

एर, हम्म ... अगर हमारे पास स्टैक पॉइंटर नहीं है, तो हम उस पते पर कुछ स्थानांतरित नहीं कर सकते हैं जो इसे इंगित करता है। शायद हम एक पॉइंटर का उपयोग कर सकते हैं जो शीर्ष के बजाय आधार को इंगित करता है।

...[ ][ ][ ][ ]...                       char* bp; // "base pointer"
    ^- bp                                bp = malloc(...);

...[a][ ][ ][ ]...        Push(a)        *bp = 'a';
    ^- bp
// No stack pointer, so no need to update it.
...[b][ ][ ][ ]...        Push(b)        *bp = 'b';
    ^- bp

उह ओह। चूंकि हम स्टैक के आधार के निश्चित मूल्य को बदल नहीं सकते हैं, हम केवल उसी स्थान पर aधक्का देकर ओवरवोट करते हैं b

ठीक है, हम क्यों नहीं ट्रैक करते हैं कि हमने कितनी बार धक्का दिया है। और हमें उस समय का भी ट्रैक रखना होगा जो हमने पॉपप किए हैं।

...[ ][ ][ ][ ]...                       char* bp; // "base pointer"
    ^- bp                                bp = malloc(...);
                                         int count = 0;

...[a][ ][ ][ ]...        Push(a)        bp[count] = 'a';
    ^- bp
...[a][ ][ ][ ]...                       ++count;
    ^- bp
...[a][b][ ][ ]...        Push(a)        bp[count] = 'b';
    ^- bp
...[a][b][ ][ ]...                       ++count;
    ^- bp
...[a][b][ ][ ]...        Pop()          --count;
    ^- bp
...[a][b][ ][ ]...                       return bp[count]; //returns b
    ^- bp

वैसे यह काम करता है, लेकिन यह वास्तव में पहले जैसा ही है, इसके अलावा (कोई अतिरिक्त अंकगणित) की *pointerतुलना में सस्ता है pointer[offset], यह उल्लेख करने के लिए नहीं है कि यह टाइप करने के लिए कम है। यह मुझे एक नुकसान की तरह लगता है।

चलो फिर से कोशिश करें। सरणी-आधारित संग्रह के अंत को खोजने के पास्कल स्ट्रिंग शैली का उपयोग करने के बजाय (संग्रह में कितने आइटम हैं, इस पर नज़र रखना), आइए C स्ट्रिंग शैली (शुरुआत से अंत तक स्कैन करें) आज़माएँ:

...[ ][ ][ ][ ]...                       char* bp; // "base pointer"
    ^- bp                                bp = malloc(...);

...[ ][ ][ ][ ]...        Push(a)        char* top = bp;
    ^- bp, top
                                         while(*top != 0) { ++top; }
...[ ][ ][ ][a]...                       *top = 'a';
    ^- bp    ^- top

...[ ][ ][ ][ ]...        Pop()          char* top = bp;
    ^- bp, top
                                         while(*top != 0) { ++top; }
...[ ][ ][ ][a]...                       --top;
    ^- bp       ^- top                   return *top; // returns '('

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

यह तय करना काफी आसान है, हमें बस परिचालन को जोड़ना है Pushऔर Popयह सुनिश्चित करने के लिए कि स्टैक के शीर्ष को हमेशा एक के साथ चिह्नित करने के लिए अपडेट किया जाता है 0, और हमें ऐसे टर्मिनेटर के साथ स्टैक को इनिशियलाइज़ करना होगा। बेशक इसका मतलब यह भी है कि 0स्टैक में वास्तव में मूल्य के रूप में हमारे पास (या जो भी मूल्य हम टर्मिनेटर के रूप में उठाते हैं) नहीं हो सकते हैं ।

उसके शीर्ष पर, हमने O (n) संचालन में O (1) ऑपरेशन क्या थे, इसे भी बदल दिया है।

टी एल; डॉ

स्टैक पॉइंटर स्टैक के शीर्ष पर नज़र रखता है, जहां सभी कार्रवाई होती है। वहाँ से छुटकारा पाने के तरीके हैं ( bp[count]और topअनिवार्य रूप से अभी भी स्टैक पॉइंटर हैं), लेकिन वे दोनों अंत में स्टैक पॉइंटर होने की तुलना में अधिक जटिल और धीमे होते हैं। और यह नहीं पता है कि स्टैक के शीर्ष का मतलब है कि आप स्टैक का उपयोग नहीं कर सकते हैं।

नोट: x86 में रनटाइम स्टैक के "नीचे" की ओर इशारा करते हुए स्टैक पॉइंटर पूरे रनटाइम स्टैक से संबंधित गलत धारणा हो सकती है। दूसरे शब्दों में, स्टैक का आधार एक उच्च मेमोरी पते पर रखा गया है, और स्टैक की नोक निचले मेमोरी एड्रेस में बढ़ती है। स्टैक पॉइंटर स्टैक की नोक पर इंगित करता है जहां सभी क्रिया होती है, बस स्टैक के आधार से कम स्मृति पते पर वह टिप होती है।


2

स्टैक पॉइंटर का उपयोग कॉल स्टैक के लिए ( फ्रेम पॉइंटर के साथ) किया जाता है ( विकिपीडिया के लिंक का पालन करें, जहां एक अच्छी तस्वीर है)।

कॉल स्टैक कॉल फ्रेम, जो वापसी पता, स्थानीय चर और अन्य स्थानीय डेटा होते हैं (विशेष रूप से, शामिल गिरा ; औपचारिक रजिस्टरों की सामग्री)।

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

इसके अलावा, सी में कुछ सरल फ़ंक्शन (कोड) को कोड करें, फिर gcc -Wall -O -S -fverbose-asm इसे संकलित करने के लिए उपयोग करें, और उत्पन्न .s कोडांतरक फ़ाइल को देखें।

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

ध्यान दें कि कॉलिंग कन्वेंशन, एबीआई और स्टैक लेआउट 32 बिट्स i686 और 64 बिट्स x86-64 पर अलग हैं। इसके अलावा, कॉलिंग कन्वेंशन (और कॉल फ्रेम को आवंटित या पॉप करने के लिए कौन जिम्मेदार है) अलग-अलग भाषाओं के साथ अलग हो सकता है (जैसे सी, पास्कल, ओकेमेल, एसबीसीएल कॉमन लिस्प में अलग-अलग कॉलिंग कन्वेंशन हैं ...)

BTW, AVX जैसे हालिया x86 एक्सटेंशन स्टैक पॉइंटर (IIRC) पर तेजी से बड़े संरेखण बाधाओं को लगा रहे हैं, x86-64 पर एक कॉल फ्रेम 16 बाइट्स, यानी दो शब्दों या बिंदुओं के साथ संरेखित होना चाहता है।


1
आप यह उल्लेख करना चाह सकते हैं कि x86-64 पर 16 बाइट्स को संरेखित करने का मतलब एक पॉइंटर के आकार / संरेखण से दोगुना है, जो वास्तव में बाइट-काउंट से अधिक दिलचस्प है।
डिडुप्लीकेटर

1

सरल शब्दों में, कार्यक्रम की परवाह है क्योंकि यह उस डेटा का उपयोग कर रहा है और इसे ढूंढने के लिए ट्रैक रखने की आवश्यकता है।

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

एसपी के बिना, संरचित प्रोग्रामिंग जैसा कि हम जानते हैं कि यह अनिवार्य रूप से असंभव होगा। (आप इसे नहीं होने के चारों ओर काम कर सकते हैं, लेकिन यह बहुत ही इसके स्वयं के संस्करण को लागू करने की आवश्यकता होगी, इसलिए यह बहुत अंतर नहीं है।)


1
आपका दावा है कि स्टैक के बिना प्रोग्रामिंग करना असंभव होगा, गलत है। निरंतरता वाली शैली में संकलित कार्यक्रम बिना किसी स्टैक का उपभोग करते हैं, लेकिन वे पूरी तरह से समझदार कार्यक्रम हैं।
एरिक लिपर्ट

@ EricLippert: "पूरी तरह से समझदार" के मूल्यों के लिए पर्याप्त रूप से निरर्थक है कि वे एक के सिर पर खड़े होते हैं और अपने आप को बाहर की ओर मोड़ते हैं, शायद। ;-)
मेसन व्हीलर 20

1
निरंतरता बीतने के साथ , कॉल स्टैक की बिल्कुल भी आवश्यकता नहीं है। प्रभावी रूप से, प्रत्येक कॉल रिटर्न के बजाय टेल कॉल और गोटो है। "जैसा कि CPS और TCO एक निहित फ़ंक्शन रिटर्न की अवधारणा को समाप्त करते हैं, उनका संयुक्त उपयोग रन-टाइम स्टैक की आवश्यकता को समाप्त कर सकता है।"

@ मिचेल्ट: मैंने एक कारण के लिए "अनिवार्य रूप से" असंभव कहा। सीपीएस सैद्धांतिक रूप से इसे पूरा कर सकता है, लेकिन व्यवहार में यह सीपीएस में किसी भी जटिलता के वास्तविक-विश्व कोड को लिखने के लिए बहुत जल्दी हास्यास्पद हो जाता है, जैसा कि एरिक ने विषय पर ब्लॉग पोस्टों की एक श्रृंखला में बताया है
मेसन व्हीलर

1
@MasonWheeler एरिक सीपीएस में संकलित कार्यक्रमों के बारे में बात कर रहा है । उदाहरण के लिए, जॉन हैरोप के ब्लॉग के हवाले से : In fact, some compilers don’t even use stack frames [...], and other compilers like SML/NJ convert every call into continuation style and put stack frames on the heap, splitting every segment of code between a pair of function calls in the source into its own separate function in the compiled form.यह "[स्टैक] के अपने स्वयं के संस्करण को लागू करने" से अलग है।
डोभाल

1

एक x86 प्रोसेसर में प्रोसेसर स्टैक के लिए, व्यंजनों के ढेर का सादृश्य वास्तव में गलत है।
विभिन्न कारणों (ज्यादातर ऐतिहासिक) के लिए, प्रोसेसर स्टैक मेमोरी के शीर्ष से मेमोरी के निचले भाग की ओर बढ़ता है, इसलिए छत से लटकने वाले चेन लिंक का एक बेहतर सादृश्य होगा। स्टैक पर कुछ धक्का देने पर, एक चेन लिंक सबसे कम लिंक में जुड़ जाता है।

स्टैक पॉइंटर चेन की सबसे निचली लिंक को संदर्भित करता है और इसका उपयोग प्रोसेसर द्वारा "देखने" के लिए किया जाता है जहां वह सबसे कम लिंक होता है, जिससे कि पूरी श्रृंखला को छत से नीचे की ओर जाने के बिना लिंक को जोड़ा या हटाया जा सकता है।

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


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


1
The stack pointer refers to the lowest link of the chain and is used by the processor to "see" where that lowest link is, so that links can be added or removed without having to travel the entire chain from the ceiling down.मुझे यकीन नहीं है कि यह एक अच्छा सादृश्य है। वास्तविकता में लिंक को कभी जोड़ा या हटाया नहीं जाता है। स्टैक पॉइंटर एक टेप के एक बिट की तरह है जो आप किसी एक लिंक को चिह्नित करने के लिए उपयोग करते हैं। यदि आप उस टेप को खो देते हैं, तो आपके पास यह जानने का कोई तरीका नहीं होगा कि आप सबसे नीचे का लिंक कौन सा इस्तेमाल करते थे ; छत से नीचे श्रृंखला की यात्रा करने से आपको मदद नहीं मिलेगी।
डोभाल

तो स्टैक पॉइंटर एक संदर्भ बिंदु प्रदान करता है जो प्रोग्राम / कंप्यूटर किसी फ़ंक्शन के स्थानीय चर को खोजने के लिए उपयोग कर सकता है?
चंद्रमन 239

यदि ऐसा है, तो कंप्यूटर स्थानीय चर कैसे खोजता है? क्या यह नीचे से ऊपर तक हर मेमोरी एड्रेस को खोजता है?
चंद्रमन 239

@ moonman239: नहीं, संकलन करते समय, कंपाइलर का ध्यान रखता है कि स्टैक पॉइंटर के सापेक्ष प्रत्येक चर कहाँ संग्रहीत किया जाता है। प्रोसेसर ऐसे सापेक्ष पते को समझता है जो चर तक सीधी पहुंच प्रदान करते हैं।
बार्ट वैन इनगेन शेनौ

1
@BartvanIngenSchenau आह, ठीक है। जब आप कहीं बाहर होते हैं और आपको मदद की ज़रूरत होती है, तो आप 911 देते हैं, जिससे आपको अंदाज़ा हो जाता है कि आप एक लैंडमार्क के सापेक्ष कहाँ हैं। स्टैक पॉइंटर, इस मामले में, आमतौर पर निकटतम "लैंडमार्क" है और इसलिए, शायद, सबसे अच्छा संदर्भ बिंदु।
चंद्रमन 239

1

यह उत्तर विशेष रूप से वर्तमान थ्रेड (निष्पादन के) के स्टैक पॉइंटर को संदर्भित करता है ।

प्रक्रियात्मक प्रोग्रामिंग भाषाओं में, एक धागे में आमतौर पर निम्नलिखित उद्देश्यों के लिए एक स्टैक 1 तक पहुंच होती है:

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

नोट 1 : थ्रेड के उपयोग के लिए समर्पित है, हालांकि इसकी सामग्री पूरी तरह से पठनीय है - और स्मैशबल - अन्य थ्रेड्स द्वारा।

असेंबली प्रोग्रामिंग, सी और सी ++ में, सभी तीन उद्देश्यों को एक ही स्टैक द्वारा पूरा किया जा सकता है। कुछ अन्य भाषाओं में, कुछ उद्देश्यों को अलग-अलग ढेर, या गतिशील रूप से आवंटित स्मृति द्वारा पूरा किया जा सकता है।


1

यहाँ स्टैक के लिए क्या उपयोग किया जाता है, इसका एक जानबूझकर ओवरसाइम्प्लीफाइड संस्करण है।

सूचकांक कार्ड के ढेर के रूप में स्टैक की कल्पना करें। स्टैक पॉइंटर शीर्ष कार्ड की ओर इशारा करता है।

जब आप किसी फ़ंक्शन को कॉल करते हैं:

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

इस बिंदु पर, फ़ंक्शन में कोड चलता है। कोड को यह जानने के लिए संकलित किया जाता है कि प्रत्येक कार्ड शीर्ष के सापेक्ष कहां है। तो यह पता है कि चर xशीर्ष से तीसरा कार्ड है (यानी स्टैक पॉइंटर - 3) और वह पैरामीटर yऊपर से छठा कार्ड है (यानी स्टैक पॉइंटर - 6.)

इस विधि का अर्थ है कि प्रत्येक स्थानीय चर या पैरामीटर के पते को कोड में बेक करने की आवश्यकता नहीं है। इसके बजाय, ये सभी डेटा आइटम स्टैक पॉइंटर के सापेक्ष संबोधित किए जाते हैं।

जब फ़ंक्शन वापस आता है, तो रिवर्स ऑपरेशन बस होता है:

  • मार्कर कार्ड के लिए देखें और इसके ऊपर के सभी कार्ड को फेंक दें। (यानी सहेजे गए पते पर स्टैक पॉइंटर सेट करें।)
  • पहले से सहेजे गए कार्ड से रजिस्टरों को पुनर्स्थापित करें और उन्हें फेंक दें। (यानी स्टैक पॉइंटर से एक निश्चित मूल्य घटाएं)
  • शीर्ष पर कार्ड पर पते से कोड चलाना शुरू करें और फिर उसे फेंक दें। (यानी स्टैक पॉइंटर से 1 घटाएं।)

स्टैक अब उस स्थिति में वापस आ गया है जब यह फ़ंक्शन कहा जाता था।

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

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


1

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

  1. कुछ कोड के बीच में, आप अपने कार्यक्रम में कुछ अन्य फ़ंक्शन कॉल कर सकते हैं ;
  2. यह फ़ंक्शन स्पष्ट रूप से नहीं जानता है कि इसे कहाँ से बुलाया गया था;
  3. फिर भी, जब इसका काम पूरा हो जाता है और यह returnनियंत्रित हो जाता है, तो नियंत्रण उस सटीक बिंदु पर वापस चला जाता है जहां कॉल शुरू किया गया था, जब कॉल शुरू किया गया था तब सभी स्थानीय परिवर्तनशील मूल्य प्रभावी थे।

कंप्यूटर उस पर नज़र कैसे रखता है? यह एक चालू रिकॉर्ड रखता है कि कौन से फ़ंक्शंस इंतज़ार कर रहे हैं कि कौन सी कॉल वापस आती है। यह रिकॉर्ड एक स्टैक है- और चूंकि यह इतना महत्वपूर्ण है, इसलिए हम इसे सामान्य रूप से स्टैक कहते हैं।

और चूंकि यह कॉल / रिटर्न पैटर्न बहुत महत्वपूर्ण है, इसलिए सीपीयू को लंबे समय से इसके लिए विशेष हार्डवेयर समर्थन प्रदान करने के लिए डिज़ाइन किया गया है। स्टैक पॉइंटर CPUs में एक हार्डवेयर विशेषता है- एक रजिस्टर जो विशेष रूप से स्टैक के शीर्ष पर नज़र रखने के लिए समर्पित है, और सीपीयू के निर्देशों का उपयोग एक सबरूटीन में शाखाओं में बंटने और उससे लौटने के लिए किया जाता है।

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