कंप्यूटर कैसे याद करते हैं कि वे चीजों को कहाँ स्टोर करते हैं?


32

जब कंप्यूटर एक वैरिएबल को स्टोर करता है, जब एक प्रोग्राम को वैरिएबल की वैल्यू प्राप्त करने की आवश्यकता होती है, तो कंप्यूटर को यह कैसे पता चलता है कि उस वैरिएबल की वैल्यू के लिए मेमोरी में कहां देखना है?


17
यह नहीं है; "कंप्यूटर" पूरी तरह से बेखबर है। हमें सभी पते को हार्डकोड करना होगा। (जो थोड़ा आसान बना रहा है, लेकिन बहुत ज्यादा नहीं।)
राफेल

1
@ राफेल: आइए इसे सामान्य करते हैं कि "हमें आधार पते को हार्डकोड करना होगा"।
phresnel

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

आपका स्रोत प्रोग्राम एक चर का उपयोग करता है। संकलक या दुभाषिया यह तय करता है कि इसे कैसे लागू किया जाए: यह कंप्यूटर को निष्पादित करने के लिए निर्देश उत्पन्न करता है और यह सुनिश्चित करना होता है कि वे घुसपैठ उन स्थानों से मूल्यों को प्राप्त करते हैं जिनमें पिछले निर्देशों ने उन्हें संग्रहीत किया था।
PJTraill

1
@ अभिरथमहिपाल: एक चर का संकलन समय पर पता नहीं होना चाहिए या समय भी नहीं चलना चाहिए; "नेमस्पेस" एक भाषा अवधारणा है जबकि एक टेबल (हैशेड या अन्यथा) एक कार्यान्वयन विवरण है; जब इसे चलाया जाता है तो कार्यक्रम में नाम की आवश्यकता बनी रहती है।
PJTraill 22

जवाबों:


31

मेरा सुझाव है कि आप संकलक निर्माण की अद्भुत दुनिया में देखो! जवाब यह है कि यह एक जटिल प्रक्रिया का एक सा है।

आपको एक अंतर्ज्ञान देने की कोशिश करने के लिए, याद रखें कि प्रोग्रामर की खातिर चर नाम शुद्ध रूप से हैं। कंप्यूटर अंत में सब कुछ पते में बदल जाएगा।

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

स्टैक पॉइंटर नामक एक छोटा सा जादू है, जो एक रजिस्टर है जो हमेशा वर्तमान स्टैक शुरू होने के पते को संग्रहीत करता है।

प्रत्येक चर को एक "स्टैक ऑफ़सेट" दिया जाता है, जो कि जहाँ स्टैक में संग्रहीत होता है। फिर, जब प्रोग्राम को एक चर का उपयोग करने की आवश्यकता होती है x, तो संकलक की जगह लेता xहै STACK_POINTER + x_offset, वास्तविक भौतिक स्थान प्राप्त करने के लिए जिसे यह मेमोरी में संग्रहीत किया जाता है।

ध्यान दें कि, यह है जब आप का उपयोग कारण है कि आप एक सूचक वापस पाने mallocया newसी या सी ++ में। आप यह निर्धारित नहीं कर सकते कि वास्तव में स्मृति में ढेर-आवंटित मूल्य कहां है, इसलिए आपको इसके लिए एक संकेतक रखना होगा। वह सूचक स्टैक पर होगा, लेकिन यह ढेर को इंगित करेगा।

फ़ंक्शन कॉल और रिटर्न के लिए स्टैक अपडेट करने का विवरण जटिल है, इसलिए यदि आप रुचि रखते हैं, तो मैं ड्रैगन बुक या टाइगर बुक को पुनः प्राप्त करूंगा ।


24

जब कंप्यूटर एक वैरिएबल को स्टोर करता है, जब एक प्रोग्राम को वैरिएबल की वैल्यू प्राप्त करने की आवश्यकता होती है, तो कंप्यूटर को यह कैसे पता चलता है कि उस वैरिएबल की वैल्यू के लिए मेमोरी में कहां देखना है?

कार्यक्रम इसे बताता है। कंप्यूटर में मूल रूप से "चर" की अवधारणा नहीं है - यह पूरी तरह से एक उच्च स्तरीय भाषा है!

यहाँ एक सी कार्यक्रम है:

int main(void)
{
    int a = 1;
    return a + 3;
}

और यहां असेंबली कोड इसका संकलन करता है: (टिप्पणियों की शुरुआत ;)

main:
    ; {
    pushq   %rbp
    movq    %rsp, %rbp

    ; int a = 1
    movl    $1, -4(%rbp)

    ; return a + 3
    movl    -4(%rbp), %eax
    addl    $3, %eax

    ; }
    popq    %rbp
    ret

"Int a = 1;" सीपीयू निर्देश को "पता 1 पर मूल्य 1 (रजिस्टर आरबीपी, माइनस 4 का मान)" देखता है। यह जानता है कि मूल्य 1 कहाँ संग्रहीत करना है क्योंकि कार्यक्रम इसे बताता है।

इसी तरह, अगला निर्देश कहता है कि "पते पर मूल्य (रजिस्टर आरबीपी का मान, माइनस 4) रजिस्टर में लोड करें"। कंप्यूटर को चर जैसी चीजों के बारे में जानने की जरूरत नहीं है।


2
जेमाइट के उत्तर से इसे जोड़ने के लिए, %rspसीपीयू का स्टैक पॉइंटर है। %rbpएक रजिस्टर है जो वर्तमान फ़ंक्शन द्वारा उपयोग किए जाने वाले स्टैक के बिट को संदर्भित करता है। दो रजिस्टरों का उपयोग डिबगिंग को सरल बनाता है।
MSALERS

2

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

प्रतीक तालिका में दर्ज पता एक रजिस्टर (जैसे स्टैक पॉइंटर) से ऑफसेट हो सकता है लेकिन यह एक कार्यान्वयन विवरण है।


0

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

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

कंप्यूटर प्रोग्राम उदाहरण

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

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

कई उच्च स्तरीय प्रोग्रामिंग भाषाओं में, यह आपके लिए सभी का ध्यान रखता है। आपको बस एक चर घोषित करना है, और उस चर में कुछ स्टोर करना है, और यह आपके लिए पर्दे के पीछे आवश्यक ढेर और सरणियां बनाता है।

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

सारांश

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

संदर्भ: कंप्यूटर कैसे याद करते हैं कि वे चीजों को कहाँ स्टोर करते हैं?


0

यह टेम्प्लेट और फॉर्मेट के कारण जानता है।

प्रोग्राम / फंक्शन / कंप्यूटर वास्तव में नहीं जानता कि कुछ भी कहाँ है। यह सिर्फ एक निश्चित स्थान पर कुछ होने की उम्मीद करता है। आइए एक उदाहरण का उपयोग करें।

class simpleClass{
    public:
        int varA=58;
        int varB=73;
        simpleClass* nextObject=NULL;
};

हमारे नए वर्ग 'सिंपलक्लास' में 3 महत्वपूर्ण चर हैं - दो पूर्णांक जिनमें कुछ डेटा हो सकते हैं जब हमें उनकी आवश्यकता होती है, और एक सूचक 'अन्य' simpleClass ऑब्जेक्ट '। मान लेते हैं कि हम सादगी के लिए 32-बिट मशीन पर हैं। 'gcc' या अन्य 'C' संकलक कुछ डेटा आवंटित करने के लिए हमारे साथ काम करने के लिए एक खाका तैयार करेंगे।

सरल प्रकार

सबसे पहले, जब कोई 'int' जैसे सरल प्रकार के लिए एक कीवर्ड का उपयोग करता है, तो निष्पादन फ़ाइल के '.data' या '.bss' खंड में कंपाइलर द्वारा एक नोट बनाया जाता है ताकि जब इसे ऑपरेटिंग सिस्टम द्वारा निष्पादित किया जाए, तो डेटा कार्यक्रम के लिए उपलब्ध है। 'Int' कीवर्ड 4 बाइट्स (32 बिट्स) आवंटित करेगा, जबकि एक 'लॉन्ग इंट' 8 बाइट्स (64 बिट्स) आवंटित करेगा।

कभी-कभी, सेल-दर-सेल तरीके से, एक चर उस निर्देश के ठीक बाद आ सकता है जिसे इसे मेमोरी में लोड करना है, इसलिए यह छद्म-असेंबली में इस तरह दिखाई देगा:

...
clear register EAX
clear register EBX
load the immediate (next) value into EAX
5
copy the value in register EAX to register EBX
...

यह EAX के साथ ही EBX में '5' मूल्य के साथ समाप्त होगा।

जबकि कार्यक्रम निष्पादित होता है, तत्काल लोड के संदर्भ के बाद से प्रत्येक निर्देश को '5' के अलावा निष्पादित किया जाता है और सीपीयू को इस पर छोड़ देता है।

इस पद्धति का नकारात्मक पक्ष यह है कि यह केवल स्थिरांक के लिए वास्तव में व्यावहारिक है, क्योंकि आपके कोड के मध्य में सरणियों / बफ़र्स / स्ट्रिंग्स को रखना अव्यावहारिक होगा। इसलिए, आम तौर पर, अधिकांश चर प्रोग्राम हेडर में रखे जाते हैं।

यदि इन गतिशील चर में से किसी एक को एक्सेस करने की आवश्यकता है, तो कोई तात्कालिक मूल्य का इलाज कर सकता है जैसे कि यह एक संकेतक था:

...
clear register EAX
clear register EBX
load the immediate value into EAX
0x0AF2CE66 (Let's say this is the address of a cell containing '5')
load the value pointed to by EAX into EBX
...

यह रजिस्टर EAX में मूल्य '0x0AF2CE66' और रजिस्टर EBX में '5' के मूल्य के साथ समाप्त होगा। एक साथ रजिस्टरों में भी मान जोड़ सकते हैं, इसलिए हम इस पद्धति का उपयोग करके किसी सरणी या स्ट्रिंग के तत्वों को खोजने में सक्षम होंगे।

एक और महत्वपूर्ण बिंदु यह है कि एक समान तरीके से पतों का उपयोग करते समय मूल्यों को संग्रहीत करने में सक्षम है, ताकि बाद में उन कोशिकाओं पर मूल्यों का संदर्भ दिया जा सके।

जटिल प्रकार

यदि हम इस वर्ग की दो वस्तुएँ बनाते हैं:

simpleClass newObjA;
simpleClass newObjB;

तब हम पहली वस्तु में इसके लिए उपलब्ध क्षेत्र के लिए दूसरी वस्तु को एक पॉइंटर दे सकते हैं:

newObjA.nextObject=&newObjB;

अब कार्यक्रम पहली वस्तु के सूचक क्षेत्र के भीतर दूसरी वस्तु का पता खोजने की उम्मीद कर सकता है। स्मृति में, यह कुछ इस तरह दिखाई देगा:

newObjA:    58
            73
            &newObjB
            ...
newObjB:    58
            73
            NULL

यहां ध्यान देने योग्य एक महत्वपूर्ण तथ्य यह है कि 'newObjA' और 'newObjB' का नाम तब नहीं होता जब वे संकलित होते हैं। वे केवल वे स्थान हैं जहाँ हम कुछ डेटा के होने की उम्मीद करते हैं। इसलिए, यदि हम & newObjA में 2 सेल जोड़ते हैं तो हम 'नेक्स्टबॉजेक्ट' के रूप में कार्य करने वाले सेल को खोजते हैं। इसलिए, यदि हम 'newObjA' का पता जानते हैं और जहाँ 'nextObject' सेल इसके सापेक्ष है, तो हम 'newObjB' का पता जान सकते हैं:

...
load the immediate value into EAX
&newObjA
add the immediate value to EAX
2
load the value in EAX into EBX

यह 'EAX' में '2 + & newObjA' और 'EBO' में 'newObjB' के साथ समाप्त होगा।

टेम्पलेट्स / प्रारूप

जब कंपाइलर वर्ग की परिभाषा को संकलित करता है, तो यह वास्तव में प्रारूप बनाने का तरीका, प्रारूप लिखने का तरीका और प्रारूप से पढ़ने का तरीका संकलित करता है।

ऊपर दिया गया उदाहरण दो 'int' चर के साथ एक एकल-लिंक-सूची के लिए एक टेम्पलेट है। द्विआधारी और एन-एरी पेड़ों के साथ गतिशील स्मृति आवंटन के लिए इस प्रकार के निर्माण बहुत महत्वपूर्ण हैं। एन-एरी ट्री के व्यावहारिक अनुप्रयोग फाइलों से बने निर्देशिकाओं से युक्त होते हैं, जो ड्राइवरों / ऑपरेटिंग सिस्टम द्वारा मान्यता प्राप्त फाइलों, निर्देशिकाओं या अन्य उदाहरणों की ओर इशारा करते हैं।

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


यहाँ प्रयुक्त शब्द 'टेम्प्लेट' और 'प्रारूप' किसी भी संकलक या संकलक की पाठ्यपुस्तक में नहीं दिखते हैं, जो मैंने कभी देखा है, और किसी भी गैर-मौजूद चीज़ के लिए दोनों शब्दों का उपयोग करने का कोई कारण नहीं दिखता है। चर के पते और / या ऑफ़सेट होते हैं, बस आपको पता होना चाहिए।
user207421

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