2009-12-18 14 views
23

मैंने प्रत्येक ब्लॉक के साथ एक वर्ग एकल ब्लॉक के उदाहरण के रूप में एक सरल, काम कर रहे टेट्रिस गेम लिखा है। > अगला संकेत -सी ++ हटाएं - यह मेरी ऑब्जेक्ट हटा देता है लेकिन मैं अभी भी डेटा तक पहुंच सकता हूं?

class SingleBlock 
{ 
    public: 
    SingleBlock(int, int); 
    ~SingleBlock(); 

    int x; 
    int y; 
    SingleBlock *next; 
}; 

class MultiBlock 
{ 
    public: 
    MultiBlock(int, int); 

    SingleBlock *c, *d, *e, *f; 
}; 

SingleBlock::SingleBlock(int a, int b) 
{ 
    x = a; 
    y = b; 
} 

SingleBlock::~SingleBlock() 
{ 
    x = 222; 
} 

MultiBlock::MultiBlock(int a, int b) 
{ 
    c = new SingleBlock (a,b); 
    d = c->next = new SingleBlock (a+10,b); 
    e = d->next = new SingleBlock (a+20,b); 
    f = e->next = new SingleBlock (a+30,b); 
} 

मैं एक समारोह है कि एक पूरी लाइन के लिए स्कैन करता है, और प्रासंगिक लोगों को हटाने और फिर नियत ब्लॉक के लिंक्ड सूची के माध्यम से चलाता है है।

SingleBlock *deleteBlock; 
SingleBlock *tempBlock; 

tempBlock = deleteBlock->next; 
delete deleteBlock; 

खेल काम करता है, ब्लॉक सही ढंग से हटा दिए जाते हैं और यह सब कुछ कार्य करता है जैसा कि माना जाता है। हालांकि निरीक्षण पर मैं अभी भी हटाए गए डेटा के यादृच्छिक बिट्स तक पहुंच सकता हूं।

यदि मैं हटाए गए सिंगलब्लॉक्स "x" मानों में से प्रत्येक को अपने विलोपन के बाद प्रिंट करता हूं, तो उनमें से कुछ यादृच्छिक कचरा (हटाने की पुष्टि) लौटाते हैं और उनमें से कुछ 222 लौटते हैं, मुझे बताते हैं कि विनाशक को डेटा कहा जाता था, वास्तव में ढेर से हटा दिया नहीं है। कई समान परीक्षण दिखाते हैं कि यह हमेशा एक ही विशिष्ट ब्लॉक होता है जो ठीक से हटाया नहीं जाता है।

परिणाम:

Existing Blocks: 
Block: 00E927A8 
Block: 00E94290 
Block: 00E942B0 
Block: 00E942D0 
Block: 00E942F0 
Block: 00E94500 
Block: 00E94520 
Block: 00E94540 
Block: 00E94560 
Block: 00E945B0 
Block: 00E945D0 
Block: 00E945F0 
Block: 00E94610 
Block: 00E94660 
Block: 00E94680 
Block: 00E946A0 

Deleting Blocks: 
Deleting ... 00E942B0, X = 15288000 
Deleting ... 00E942D0, X = 15286960 
Deleting ... 00E94520, X = 15286992 
Deleting ... 00E94540, X = 15270296 
Deleting ... 00E94560, X = 222 
Deleting ... 00E945D0, X = 15270296 
Deleting ... 00E945F0, X = 222 
Deleting ... 00E94610, X = 222 
Deleting ... 00E94660, X = 15270296 
Deleting ... 00E94680, X = 222 

कब्र की उम्मीद से परे का डेटा एक्सेस करने में सक्षम होने है?

क्षमा करें अगर यह थोड़ा लंबा हवादार है।

+1

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

+0

यदि आप ब्लॉक तक पहुंच सकते हैं, तो आप उन्हें पुनः हटा सकते हैं। यह बुरी बात है। ऐसा मत करो –

+15

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

उत्तर

69

कब्र से परे डेटा तक पहुंचने में सक्षम है?

यह तकनीकी रूप से अपरिभाषित व्यवहार के रूप में जाना जाता है। आश्चर्यचकित न हों अगर यह आपको बियर का एक कैन भी प्रदान करता है।

+9

इसके अलावा, उस तथ्य की समाप्ति को जोड़ना अच्छा होता है ... अगर किसी के पास डेटा में "संवेदनशील" संग्रहीत डेटा होता है, तो किसी को यह समझना चाहिए कि इसे हटाने से पहले इसे पूरी तरह से ओवरराइट करना एक अच्छा अभ्यास है (दूसरे को रोकने के लिए इसे एक्सेस करने से कोड के खंड)। – Romain

+0

इसे डॉटोर कॉल से पहले संभाला जाना चाहिए। – dirkgently

+1

... या कम से कम dtor में। – dirkgently

0

यह अभी तक स्मृति को शून्य/बदल नहीं देगा ... लेकिन किसी बिंदु पर, गलीचा आपके पैरों के नीचे से खींचा जा रहा है।

नहीं, यह निश्चित रूप से अनुमानित नहीं है: यह इस बात पर निर्भर करता है कि कितनी तेजी से स्मृति आवंटन/डेलोकेशन मंथन किया जाता है।

28

कब्र से परे डेटा तक पहुंचने में सक्षम है?

ज्यादातर मामलों में, हाँ। कॉलिंग डिलीट स्मृति को शून्य नहीं करता है।

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

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

+0

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

+0

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

+4

@ थॉमस मैथ्यू मैं यह नहीं कह रहा हूं कि इसे एक्सेस करने का प्रयास करना एक अच्छा विचार है। @ कर्ट निकोल यह शब्दों के साथ खेल रहा है। आपके द्वारा उपयोग किए जाने वाले कंपाइलर के आधार पर, आप * उम्मीद कर सकते हैं कि हटाए जाने पर स्मृति को तुरंत शून्य नहीं किया गया है। हालांकि आप इसके बारे में निश्चित रूप से सुनिश्चित नहीं हो सकते हैं। – Martin

1

मेमोरी को हटा देता है, लेकिन इसे संशोधित नहीं करता है या इसे शून्य नहीं करता है। फिर भी आपको विलुप्त स्मृति तक नहीं पहुंचना चाहिए।

9

सी ++ कॉल अपरिभाषित व्यवहार है - आप डेटा तक पहुंचने में सक्षम हो सकते हैं, हो सकता है कि आप नहीं कर सकते। किसी भी मामले में, यह करना गलत बात है।

2

जब आप इसे delete() के माध्यम से रिलीज़ करते हैं तो सिस्टम स्मृति को साफ़ नहीं करता है। तब तक सामग्री तब तक पहुंच योग्य होती है जब तक स्मृति को पुन: उपयोग और ओवरराइट करने के लिए असाइन नहीं किया जाता है।

1

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

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

0

हां, कभी-कभी इसकी उम्मीद की जा सकती है। जबकि new डेटा के लिए स्थान आरक्षित करता है, deletenew के साथ बनाए गए पॉइंटर को अमान्य करता है, जिससे पहले आरक्षित स्थानों पर डेटा लिखा जा सकता है; यह आवश्यक रूप से डेटा को हटा नहीं है। हालांकि, आपको उस व्यवहार पर भरोसा नहीं करना चाहिए क्योंकि उन स्थानों पर डेटा किसी भी समय बदल सकता है, संभवतः आपके प्रोग्राम को गलत व्यवहार करने का कारण बन सकता है। यही कारण है कि आप को new[] के साथ आवंटित सरणी पर एक सूचक (या delete[]) पर उपयोग करने के बाद, आपको इसे पूर्ण असाइन करना चाहिए ताकि आप अमान्य सूचक के साथ छेड़छाड़ नहीं कर सकें, मानते हैं कि आप new या new[] का उपयोग करके स्मृति आवंटित नहीं करेंगे उस सूचक का दोबारा उपयोग करने से पहले।

+3

सी ++ भाषा मानक में कुछ भी नहीं है जो हटाए गए मेमोरी को मिटाने या अजीब मूल्य से भरने से' हटाएं 'को रोकता है। यह कार्यान्वयन परिभाषित किया गया है। –

8

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

template< class T > void Delete(T*& pointer) 
{ 
    delete pointer; 
    pointer = NULL; 
} 

यह गलती से अवैध स्मृति का उपयोग करने से रोकता:

इस संबंध है कि अक्सर पुस्तकालयों में सामना करना पड़ा है में प्रथाओं में से एक एक हटाएँ कार्य है।

ध्यान दें कि delete NULL; पर कॉल करना बिल्कुल ठीक है।

+0

भले ही आप मैक्रो का उपयोग न करें, फिर भी इसे मुक्त करने के तुरंत बाद एक पॉइंटर को NULL पर सेट करना अच्छा अभ्यास है। इस तरह के गलतफहमी को रोकने में, यह एक अच्छी आदत है। –

+1

@ कॉर्नेल कोई भी सी ++ लाइब्रेरी जो इस तरह के मैक्रो का उपयोग करती है, बेहद संदिग्ध, आईएमएचओ होगी। बहुत पसीने पर, यह एक इनलाइन टेम्पलेट समारोह होना चाहिए। –

+8

@ मार्क सेटिंग पॉइंटर्स को निम्नलिखित के बाद हटाएं सी ++ में सार्वभौमिक अच्छा अभ्यास नहीं है। ऐसे समय होते हैं जब यह करना एक अच्छी बात है, और कई बार जब यह व्यर्थ है और त्रुटियों को छुपा सकता है। –

1

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

आप यह तो बाहर शून्य बनाना चाहते हैं कार्य करें:

SingleBlock::~SingleBlock() 

{ x = y = 0 ; } 
3

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

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

+1

यदि कोई कंपाइलर यह निर्धारित कर सकता है कि कोड अनिवार्य रूप से ब्लैकबोर्ड के हिस्से तक पहुंचने के लिए जा रहा है (यहां तक ​​कि देखें), तो यह दृढ़ संकल्प समय और कारक के नियमों से संकलक को मुक्त नहीं करेगा; कुछ कंपाइलर्स इसका फायदा उठाते हैं कि एक दशक पहले बेतुका माना जाता था (जिनमें से कई अभी भी बेतुका हैं, आईएमएचओ)। मैं यह कहकर समझ सकता था कि यदि कोड के दो टुकड़े एक-दूसरे पर निर्भर नहीं हैं तो एक कंपाइलर किसी भी फैशन में अपनी प्रसंस्करण को अंतःस्थापित कर सकता है, भले ही यूबी को "प्रारंभिक" हिट करने का कारण बन जाए, लेकिन एक बार जब यूबी अनिवार्य हो जाए तो सभी नियम खिड़की से बाहर निकल जाएंगे। – supercat

1

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

मेरा सुझाव है कि यदि आप new/delete और कच्चे संकेत (बजाय std::make_shared() और इसी तरह) के साथ कोड लिखने, कि आप वेलग्रिंड के तहत अपने इकाई परीक्षण का प्रयोग कम से कम ऐसी त्रुटियों खोलना का एक मौका है।

1

ठीक है, मैं इसके बारे में कुछ समय के लिए भी सोच रहा हूं, और मैंने कुछ परीक्षण चलाने की कोशिश की है ताकि यह समझ सके कि हुड के नीचे क्या हो रहा है। मानक उत्तर यह है कि पर कॉल करने के बाद हटाएं आपको उस मेमोरी स्पॉट तक पहुंचने से कुछ भी अच्छा नहीं चाहिए। हालांकि, यह मेरे लिए पर्याप्त प्रतीत नहीं हुआ। को कॉल करते समय वास्तव में क्या हो रहा है (पीआरटी)? मुझे जो मिला है वह यहां है। मैं उबंटू 16.04 पर जी ++ का उपयोग कर रहा हूं, इसलिए यह परिणामों में एक भूमिका निभा सकता है।

डिलीट ऑपरेटर का उपयोग करते समय मैंने पहली बार अपेक्षा की थी कि मुक्त प्रक्रिया को अन्य प्रक्रियाओं में उपयोग के लिए सिस्टम को वापस सौंप दिया जाएगा। मुझे कहना है कि यह किसी भी परिस्थिति में मैंने नहीं किया है।

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

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

तो, स्पष्ट रूप से एक प्रोग्राम द्वारा उपयोग की जाने वाली स्मृति में वृद्धि हो सकती है, लेकिन कभी भी को कम नहीं किया जा सकता है। मैंने सोचा था कि शायद महत्वपूर्ण परिस्थितियों में अन्य प्रक्रियाओं के लिए स्मृति जारी की जाएगी, जैसे कि जब कोई और स्मृति उपलब्ध न हो। आखिरकार, किसी प्रोग्राम को हमेशा अपनी याददाश्त रखने के लिए क्या अर्थ होगा, जब अन्य प्रक्रियाएं इसके लिए पूछ रही हों? इसलिए मैंने 30 एमबी आवंटित किया, और उन्हें को हटाते समय मैं memtester चलाता हूं जितना भौतिक स्मृति मैं कर सकता था। मुझे उम्मीद है कि मेरे सॉफ़्टवेयर को मेमटेस्टर को अपनी याददाश्त सौंपने की उम्मीद है। लेकिन अनुमान लगाओ, ऐसा नहीं हुआ!

मैं एक छोटी स्क्रीनकास्ट कि कार्रवाई में बात से पता चलता बना दिया है:

Delete example memory

100% ईमानदारी से कहूं तो, वहाँ एक स्थिति है जिसमें कुछ हुआ था। जब मैंने अपने कार्यक्रम की विलुप्त होने की प्रक्रिया के मध्य में उपलब्ध भौतिक स्मृति से अधिक के साथ मेमटेस्टर की कोशिश की, तो मेरे कार्यक्रम द्वारा उपयोग की जाने वाली स्मृति लगभग 3 एमबी तक गिर गई। हालांकि Memtester प्रक्रिया स्वचालित रूप से मारा गया था, और क्या हुआ और भी आश्चर्य की बात थी! मेरे प्रोग्राम का मेमोरी उपयोग प्रत्येक डिलीट कॉल के साथ बढ़ गया! ऐसा ही था जैसे उबंटू यादगार घटना के बाद अपनी सारी याददाश्त बहाल कर रहा था।

http://www.thecrowned.org/c-delete-operator-really-frees-memory

संबंधित मुद्दे