c++ - क्या स्थानीय चर की स्मृति को इसके दायरे से बाहर किया जा सकता है?




memory-management local-variables (14)

मेरे पास निम्न कोड है।

int * foo()
{
    int a = 5;
    return &a;
}

int main()
{
    int* p = foo();
    cout << *p;
    *p = 8;
    cout << *p;
}

और कोड सिर्फ रनटाइम अपवादों के साथ चल रहा है!

उत्पादन 58 था

यह कैसे हो सकता है? क्या स्थानीय चर की स्मृति इसके कार्य के बाहर पहुंच योग्य नहीं है?


यह कैसे हो सकता है? क्या स्थानीय चर की स्मृति इसके कार्य के बाहर पहुंच योग्य नहीं है?

आप एक होटल का कमरा किराए पर लेते हैं। आपने बेडसाइड टेबल के शीर्ष दराज में एक पुस्तक रखी और सो जाओ। आप अगली सुबह देखें, लेकिन अपनी कुंजी वापस देने के लिए "भूल जाओ"। आप चाबी चोरी करते हैं!

एक हफ्ते बाद, आप होटल लौटते हैं, चेक-इन नहीं करते हैं, अपने पुराने कमरे में चुराया जाता है और चोरी के साथ देखते हैं। आपकी पुस्तक अभी भी वहां है। आश्चर्यजनक!

ऐसे कैसे हो सकता है? यदि आप कमरे किराए पर नहीं लेते हैं तो होटल के कमरे के दराज की सामग्री उपलब्ध नहीं है?

खैर, जाहिर है कि परिदृश्य वास्तविक दुनिया में कोई समस्या नहीं हो सकती है। कोई रहस्यमय बल नहीं है जो आपकी पुस्तक को गायब होने का कारण बनता है जब आप कमरे में रहने के लिए अधिकृत नहीं होते हैं। न ही एक रहस्यमय बल है जो आपको चोरी की कुंजी के साथ कमरे में प्रवेश करने से रोकता है।

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

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

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

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

अद्यतन करें

पवित्र भलाई, यह जवाब बहुत ध्यान दे रहा है। (मुझे यकीन नहीं है कि क्यों - मैंने इसे सिर्फ "मजेदार" थोड़ा सा समानता माना, लेकिन जो भी हो।)

मैंने सोचा कि यह कुछ और तकनीकी विचारों के साथ थोड़ा सा अपडेट करने के लिए जर्मनी हो सकता है।

कंपाइलर कोड उत्पन्न करने के व्यवसाय में हैं जो उस कार्यक्रम द्वारा छेड़छाड़ किए गए डेटा के संग्रहण का प्रबंधन करता है। मेमोरी को प्रबंधित करने के लिए कोड उत्पन्न करने के कई अलग-अलग तरीके हैं, लेकिन समय के साथ दो मूल तकनीकें बढ़ी हैं।

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

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

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

इस कारण से, स्थानीय चर आमतौर पर "स्टैक" डेटा संरचना पर भंडारण के रूप में उत्पन्न होते हैं, क्योंकि एक ढेर में संपत्ति होती है जिस पर पहली चीज धक्का देती है, आखिरी चीज पॉप-अप हो जाती है।

ऐसा लगता है कि होटल केवल अनुक्रमिक रूप से कमरे किराए पर लेने का फैसला करता है, और आप तब तक जांच नहीं सकते जब तक कि आपके द्वारा चेक आउट किए गए कमरे की संख्या वाले सभी लोग न हों।

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

हम अस्थायी स्टोर के लिए ढेर का उपयोग करते हैं क्योंकि वे वास्तव में सस्ते और आसान हैं। स्थानीय लोगों के भंडारण के लिए एक ढेर का उपयोग करने के लिए सी ++ के कार्यान्वयन की आवश्यकता नहीं है; यह ढेर का उपयोग कर सकते हैं। ऐसा नहीं है, क्योंकि इससे कार्यक्रम धीमा हो जाएगा।

सी ++ के कार्यान्वयन को आपके द्वारा छोड़े गए कचरे को छोड़ने के लिए आवश्यक नहीं है ताकि आप इसके बाद अवैध रूप से वापस आ सकें; यह संकलक को कोड उत्पन्न करने के लिए पूरी तरह से कानूनी है जो आपके द्वारा खाली किए गए "कमरे" में शून्य सब कुछ वापस लौटाता है। ऐसा इसलिए नहीं है क्योंकि यह महंगा होगा।

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

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

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

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

आगे पढ़ने के लिए:

  • क्या होगा अगर सी # ने संदर्भों को वापस करने की अनुमति दी? संयोग से यह आज के ब्लॉग पोस्ट का विषय है:

    http://blogs.msdn.com/b/ericlippert/archive/2011/06/23/ref-returns-and-ref-locals.aspx

  • स्मृति का प्रबंधन करने के लिए हम ढेर का उपयोग क्यों करते हैं? क्या सी # में मूल्य प्रकार हमेशा स्टैक पर संग्रहीत होते हैं? वर्चुअल मेमोरी कैसे काम करती है? और सी # मेमोरी मैनेजर कैसे काम करता है में कई और विषय। इनमें से कई लेख सी ++ प्रोग्रामर के लिए जर्मन भी हैं:

    https://blogs.msdn.microsoft.com/ericlippert/tag/memory-management/


अवैध स्मृति तक पहुंचने से आपने कभी भी C ++ अपवाद नहीं फेंक दिया। आप केवल मनमाने ढंग से स्मृति स्थान का संदर्भ देने के सामान्य विचार का एक उदाहरण दे रहे हैं। मैं ऐसा ही कर सकता था:

unsigned int q = 123456;

*(double*)(q) = 1.2;

यहां मैं बस 123456 का इलाज डबल के पते के रूप में कर रहा हूं और इसे लिखता हूं। कुछ भी चीजें हो सकती हैं:

  1. q वास्तव में वास्तव में एक डबल का एक मान्य पता हो सकता है, उदाहरण के लिए double p; q = &p; double p; q = &p;
  2. q कहीं आवंटित स्मृति के अंदर इंगित कर सकता है और मैं वहां 8 बाइट्स को ओवरराइट करता हूं।
  3. आवंटित स्मृति के बाहर q अंक और ऑपरेटिंग सिस्टम के मेमोरी मैनेजर मेरे प्रोग्राम में सेगमेंटेशन फॉल्ट सिग्नल भेजता है, जिससे रनटाइम इसे समाप्त कर देता है।
  4. आप लॉटरी जीतते हैं।

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

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


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

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

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

संक्षेप में: यह आमतौर पर काम नहीं करेगा, लेकिन कभी-कभी मौके से होगा।


आपकी समस्या के दायरे से कोई लेना देना नहीं है। आपके द्वारा दिखाए गए कोड में, फ़ंक्शन main फ़ंक्शन foo में नाम नहीं देखता है, इसलिए आप foo के बाहर इस नाम के साथ सीधे foo तक नहीं पहुंच सकते हैं।

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


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

तो, यहां फ़ंक्शन foo() का पता लौटा रहा a और उसका पता लौटने के बाद नष्ट हो गया है। और आप उस लौटे पते के माध्यम से संशोधित मूल्य तक पहुंच सकते हैं।

मुझे असली दुनिया का उदाहरण लेने दो:

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


क्या आपने ऑप्टिमाइज़र सक्षम के साथ प्रोग्राम को संकलित किया था?

Foo () फ़ंक्शन काफी सरल है और परिणामी कोड में इनलाइन / प्रतिस्थापित किया गया हो सकता है।

लेकिन मैं मार्क बी के साथ सहमत हूं कि परिणामी व्यवहार अपरिभाषित है।


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

हालांकि, यह अपरिभाषित व्यवहार है और आपको काम पर भरोसा नहीं करना चाहिए!


यह कर सकता है, क्योंकि a अस्थायी रूप से अपने दायरे ( foo फ़ंक्शन) के जीवनकाल के लिए आवंटित एक चर है। आप foo से वापस आने के बाद स्मृति मुक्त है और ओवरराइट किया जा सकता है।

आप जो कर रहे हैं उसे अपरिभाषित व्यवहार के रूप में वर्णित किया गया है । परिणाम की भविष्यवाणी नहीं की जा सकती है।


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


यह निश्चित रूप से एक समय मुद्दा है! ऑब्जेक्ट जो p पॉइंटर इंगित करता है वह "निर्धारित" होता है जब foo के दायरे से बाहर निकलता है। हालांकि, यह ऑपरेशन तुरंत नहीं होता है, बल्कि बाद में कई CPU चक्रों का होता है। चाहे यह अपरिभाषित व्यवहार है, या सी ++ वास्तव में पृष्ठभूमि में कुछ पूर्व-सफाई सामग्री कर रहा है, मुझे नहीं पता।

यदि आप अपने ऑपरेटिंग सिस्टम के sleep समारोह में कॉल को foo और cout स्टेटमेंट्स के बीच कॉल cout , तो प्रोग्राम को पॉइंटर को डिफ्रेंस करने से पहले एक या दूसरी प्रतीक्षा करें, तो आप देखेंगे कि जब तक आप इसे पढ़ना चाहते हैं तब तक डेटा चला जाता है ! मेरा उदाहरण देखें:

#include <iostream>
#include <unistd.h>
using namespace std;

class myClass {
public:
    myClass() : i{5} {
        cout << "myClass ctor" << endl;
    }

    ~myClass() {
        cout << "myClass dtor" << endl;
    }

    int i;
};

myClass* foo() {
    myClass a;
    return &a;
}

int main() {

    bool doSleep{false};

    auto p = foo();

    if (doSleep) sleep(1);

    cout << p->i << endl;
    p->i = 8;
    cout << p->i << endl;
}

(ध्यान दें कि मैंने unistd.h से sleep फ़ंक्शन का उपयोग किया है, जो केवल यूनिक्स-जैसी प्रणालियों पर मौजूद है, इसलिए यदि आप विंडोज़ पर हैं तो आपको इसे Sleep(1000) और Windows.h साथ बदलना होगा।)

मैंने अपनी int को कक्षा के साथ बदल दिया, इसलिए मैं बिल्कुल देख सकता हूं कि विनाशक कहलाता है।

इस कोड का आउटपुट निम्न है:

myClass ctor
myClass dtor
5
8

हालांकि, अगर आप true बदलते हैं:

myClass ctor
myClass dtor
0
8

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

यह बहुत अजीब है, क्योंकि विनाशक को दायरे से बाहर निकलने के तुरंत बाद बुलाया जाता है, हालांकि, वास्तविक विनाश थोड़ा देरी हो रही है।

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

या कोई अन्य कारण गालदार कंपाइलर हो सकता है जो मानक का पालन नहीं करते हैं। दरअसल यह एकमात्र ऐसा मामला नहीं होगा जहां कंपेलर अतिरिक्त प्रदर्शन के लिए मानक अनुरूपता का थोड़ा सा व्यापार करते हैं!

इसका जो भी कारण है, यह स्पष्ट है कि डेटा नष्ट हो गया है, बस तुरंत नहीं।


यह स्मृति पते का उपयोग करने का 'गंदा' तरीका है। जब आप एक पता (सूचक) वापस करते हैं तो आप नहीं जानते कि यह किसी फ़ंक्शन के स्थानीय दायरे से संबंधित है या नहीं। यह सिर्फ एक पता है। अब जब आपने 'foo' फ़ंक्शन का आह्वान किया है, तो 'ए' का पता (स्मृति स्थान) पहले से ही आपके आवेदन (प्रक्रिया) की पता योग्य स्मृति (सुरक्षित रूप से, कम से कम) में आवंटित किया गया था। 'Foo' फ़ंक्शन लौटने के बाद, 'ए' के ​​पते को 'गंदे' माना जा सकता है लेकिन यह वहां है, प्रोग्राम के दूसरे भाग में कम से कम अभिव्यक्तियों द्वारा साफ़ नहीं किया गया है, न ही परेशान / संशोधित किया गया है (कम से कम इस विशिष्ट मामले में)। एसी / सी ++ कंपाइलर आपको ऐसी 'गंदे' पहुंच से नहीं रोकता है (यदि आपको परवाह है तो भी आपको चेतावनी दे सकती है)। जब तक आप कुछ तरीकों से पते की रक्षा नहीं करते हैं, तब तक आप अपने प्रोग्राम इंस्टेंस (प्रक्रिया) के डेटा सेगमेंट में मौजूद किसी मेमोरी लोकेशन का सुरक्षित रूप से उपयोग (अपडेट) कर सकते हैं।


सभी उत्तरों के लिए थोड़ा सा अतिरिक्त:

यदि आप ऐसा कुछ करते हैं:

#include<stdio.h>
#include <stdlib.h>
int * foo(){
    int a = 5;
    return &a;
}
void boo(){
    int a = 7;

}
int main(){
    int * p = foo();
    boo();
    printf("%d\n",*p);
}

आउटपुट शायद होगा: 7

ऐसा इसलिए है क्योंकि foo () से लौटने के बाद ढेर मुक्त हो जाता है और फिर बू () द्वारा पुन: उपयोग किया जाता है। यदि आप निष्पादन योग्य को अलग करते हैं तो आप इसे स्पष्ट रूप से देखेंगे।


सही (?) कंसोल आउटपुट वाली चीजें नाटकीय रूप से बदल सकती हैं यदि आप :: printf का उपयोग करते हैं लेकिन cout नहीं। आप नीचे कोड के भीतर डीबगर के साथ खेल सकते हैं (x86, 32-बिट, MSVisual स्टूडियो पर परीक्षण):

char* foo() 
{
  char buf[10];
  ::strcpy(buf, "TEST”);
  return buf;
}

int main() 
{
  char* s = foo();    //place breakpoint & check 's' varialbe here
  ::printf("%s\n", s); 
}

सी ++ में, आप किसी भी पते तक पहुंच सकते हैं , लेकिन इसका मतलब यह नहीं है कि आपको चाहिए । जिस पते का आप उपयोग कर रहे हैं वह अब मान्य नहीं है। यह काम करता है क्योंकि फू लौटने के बाद मेमोरी को और भी खराब नहीं किया गया, लेकिन यह कई परिस्थितियों में दुर्घटनाग्रस्त हो सकता है। Valgrind साथ अपने प्रोग्राम का विश्लेषण करने का प्रयास करें, या यहां तक ​​कि इसे अनुकूलित भी संकलित करें, और देखें ...





dangling-pointer