2011-12-14 23 views
6

मुझे गतिशील रूप से आवंटित स्मृति की बात नहीं मिलती है और मुझे आशा है कि आप लोग मेरे लिए चीजों को स्पष्ट कर सकते हैं।सी ++ गतिशील रूप से आवंटित स्मृति

सबसे पहले, हर बार जब हम स्मृति आवंटित करते हैं तो हम बस उस स्मृति को एक सूचक प्राप्त करते हैं।

int * dynInt = new int; 

तो क्या मैं ऊपर किया था और के बीच अंतर क्या है:

int someInt; 
int* dynInt = &someInt; 

के रूप में मैं समझता हूँ, दोनों ही मामलों स्मृति किसी पूर्णांक के लिए आवंटित किया जाता है, और हम उस स्मृति के लिए सूचक पाने में।

तो दोनों के बीच क्या अंतर है। एक विधि को दूसरी तरफ कब पसंद किया जाता है।

इसके अलावा अधिक कारण है कि मैं पहले मामले में

delete dynInt; 

के साथ स्मृति को मुक्त करने के लिए, लेकिन दूसरे मामले में नहीं की जरूरत है।

मेरे अनुमान कर रहे हैं:

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

  2. कारण हमें दूसरे मामले के लिए हटाने का उपयोग करने की आवश्यकता नहीं है क्योंकि तथ्य यह है कि ऑब्जेक्ट प्रारंभ किया गया था, किसी प्रकार का स्वचालित विनाश दिनचर्या बनाता है।

ये अनुमान लगाएंगे कि किसी ने मुझे सही किया और मेरे लिए चीजों को स्पष्ट किया।

+3

यदि आप अनिश्चित हैं, तो सरल नियम का पालन करें: "पॉइंटर्स का कभी भी उपयोग न करें; कभी भी 'नया' का उपयोग न करें।" एक बार जब आप मैन्युअल रूप से प्रबंधित ऑब्जेक्ट जीवनकाल की आवश्यकता को समझ लेते हैं, तो आपको पता चलेगा कि इस नियम को कब तोड़ना है। –

+1

उम मैं दृढ़ता से आपको सी ++ के बारे में एक पुस्तक खोलने का आग्रह कर सकता हूं .. परिवर्तनीय स्कोप, गतिशील स्मृति प्रबंधन आमतौर पर किसी भी सी ++ शुरुआती किताबों के चौथे या 5 वें अध्याय में चर्चा की जाती है। @KerrekSB मैं यह नहीं कहूंगा .. विशेष रूप से इस मामले में अनिश्चितता अस्पष्टता से नहीं आती है, बल्कि पर्याप्त ज्ञान से नहीं - यदि आप अनिश्चित हैं तो सुनिश्चित करें कि आप पर्याप्त पढ़ते हैं ताकि आप सुनिश्चित हो सकें। – paul23

+0

गतिशील रूप से आवंटित स्मृति आमतौर पर _slower_ है, और प्रारंभिकरण के पास इनमें से किसी के साथ कुछ लेना देना नहीं है। –

उत्तर

15

अंतर संग्रहण अवधि में है। स्वत: भंडारण अवधि साथ

  • वस्तुओं अपने "सामान्य" वस्तुओं कि स्वचालित रूप से ब्लॉक जिसमें वे परिभाषित कर रहे हैं के अंत में क्षेत्र से बाहर जाना है।

    उन्हें int someInt जैसे बनाएं;

    आपने उनके बारे में "स्टैक ऑब्जेक्ट्स" के रूप में सुना होगा, हालांकि मैं ऑब्जेक्ट इस शब्दावली के लिए।

  • के साथ ऑब्जेक्ट्स गतिशील संग्रहण अवधि में "मैन्युअल" जीवनकाल का कुछ है; आपको delete के साथ स्वयं को नष्ट करना होगा, और उन्हें new कीवर्ड बनाएं।

    आपने उनके बारे में "ढेर ऑब्जेक्ट्स" के रूप में सुना होगा, हालांकि मैं इस पर भी ऑब्जेक्ट करता हूं।

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

लेकिन यह है कि आप एक स्वत: वस्तु के लिए एक सूचक चाहता हूँ, क्योंकि दुर्लभ है:

  1. आप एक "डिफ़ॉल्ट रूप से" नहीं है;
  2. ऑब्जेक्ट बहुत लंबे समय तक नहीं चल रहा है, इसलिए ऐसा पॉइंटर के साथ बहुत कुछ नहीं कर सकता है।

इसके विपरीत, गतिशील वस्तुओं को अक्सर पॉइंटर्स के माध्यम से उपयोग किया जाता है, क्योंकि सिंटैक्स इसे लागू करने के करीब आता है। new आपके लिए उपयोग करने के लिए एक सूचक देता है, आपको delete पर पॉइंटर पास करना होगा, और (संदर्भों का उपयोग करने के अलावा) ऑब्जेक्ट तक पहुंचने का कोई अन्य तरीका नहीं है। यह गतिशीलता के बादल में "वहां से बाहर" रहता है जो स्थानीय क्षेत्र में बैठे नहीं है।

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

+0

स्वचालित चर के लिए पॉइंटर्स उपयोगी हो सकते हैं जब एक फ़ंक्शन का आविष्कार किया जाता है जो उसके तर्कों को म्यूटेट करने के लिए होता है (और शायद सफलता का संकेत देने के लिए इसके वापसी मूल्य का उपयोग करें)। –

+3

@ TamásSzelei: इस मामले में आपको एक संदर्भ का उपयोग करना चाहिए। –

+0

@ TamásSzelei: ठीक है, सच है। हालांकि, यह बहुत ही स्थानीय उपयोग है। मुझे लगता है कि मैं एक सूचक प्राप्त करने और इसे संग्रहीत करने के बारे में बात कर रहा हूं, एक अभिव्यक्ति में अस्थायी का उपयोग नहीं कर रहा हूं –

13

एक इस तरह बनाई गई वस्तु:

int foo; 

स्वत: भंडारण अवधि है - वस्तु रहता है जब तक चर foo क्षेत्र से बाहर चला जाता है। इसका मतलब है कि आपके पहले उदाहरण में, dynInt एक अमान्य सूचक होगा someInt गुंजाइश से बाहर हो जाता है (उदाहरण के लिए, फ़ंक्शन के अंत में)।

एक इस तरह बनाई गई वस्तु:

int foo* = new int; 

गतिशील भंडारण अवधि है - वस्तु रहता है जब तक आप स्पष्ट रूप से उस पर delete कहते हैं।

वस्तुओं का प्रारंभ एक ऑर्थोगोनल अवधारणा है; यह सीधे आप किस प्रकार की स्टोरेज-अवधि का उपयोग करते हैं उससे संबंधित नहीं है। प्रारंभिकरण के बारे में अधिक जानकारी के लिए here देखें।

+0

में विषय को देखना होगा, इस तरह के अच्छे और स्पष्ट उत्तर के लिए धन्यवाद। एक बार फिर धन्यवाद। – vinay

+2

पेडेंटिक होने के लिए, आपको वास्तव में * मेमोरी * के बारे में कुछ भी पता नहीं है, क्योंकि यह आपके आवंटन फ़ंक्शन ':: ऑपरेटर को नया()' के कार्यान्वयन पर निर्भर करता है। यह ऑब्जेक्ट का * जीवनकाल * है जो गतिशील है, यानी मैन्युअल: * ऑब्जेक्ट * तब तक रहता है जब तक आप इसे हटा नहीं देते। सी ++ स्मृति आवंटन और वस्तु निर्माण को अलग करता है। –

+1

@ केरेकस्क: मैं ऐसा उत्तर लिखते समय हमेशा थोड़ा डरता हूं, क्योंकि मुझे कुछ गलत लगता है :) –

1

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

+0

जब तक आप [alloca] (http://stackoverflow.com/q/1018853/533120) का उपयोग नहीं करते हैं;) –

4

एक पूर्णांक के लिए यह केवल तभी समझ में आता है जब आपको उदाहरण के बाद मान रखने की आवश्यकता होती है, फ़ंक्शन से लौटना। जैसा कि आपने कहा था कि आपने someInt घोषित किया था, जैसे ही यह दायरे से बाहर हो गया था, इसे अमान्य कर दिया गया होगा।

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

unsigned char data[1000000]; 

लेकिन केवल तभी, छवि का आकार से कम या 1000000 बाइट्स के बराबर था काम करेगा, और भी छोटे आकार के चित्रों के लिए बेकार हो जाएगा।

unsigned char* data = new unsigned char[file_size]; 

यहाँ, file_size रनटाइम पर निर्धारित होता है: इसके बजाय, हम गतिशील स्मृति आवंटित कर सकते हैं। आप संकलन के समय संभवतः इस मूल्य को नहीं बता सकते थे।

+1

क्या आप अभी नहीं कर सकते: "हस्ताक्षरित चार डेटा [file_size];"? – user1066113

+0

नहीं, आप नहीं कर सके। खैर, कुछ सी संकलक इसे स्वीकार कर सकते हैं, लेकिन यह अभी भी गतिशील आवंटन है और मानक सी (89) और न ही C++; देखें [वीएलए] (http://en.wikipedia.org/wiki/Variable-length_array)। आईएसओ सी 99 वीएलए का समर्थन करता है। –

2

जब भी आप C12+ मेमोरी में new का उपयोग कर रहे हैं तो malloc के माध्यम से आवंटित किया जाता है जो sbrk सिस्टम कॉल (या इसी तरह) को कॉल करता है। इसलिए ओएस को छोड़कर, कोई भी अनुरोधित आकार के बारे में ज्ञान नहीं है। इसलिए आपको सिस्टम को स्मृति देने के लिए delete (जो free पर कॉल करता है जो sbrk पर जाता है) का उपयोग करना होगा। अन्यथा आपको एक स्मृति रिसाव मिलेगा।

अब, जब आपके दूसरे मामले की बात आती है, तो कंपाइलर को आवंटित स्मृति के आकार के बारे में ज्ञान होता है। यही है, आपके मामले में, एक int का आकार। इस int के पते पर पॉइंटर सेट करना आवश्यक स्मृति के ज्ञान में कुछ भी नहीं बदलता है। या दूसरे शब्दों के साथ: संकलक स्मृति को मुक्त करने की देखभाल करने में सक्षम है। new के साथ पहले मामले में यह संभव नहीं है।

इसके अतिरिक्त: new क्रमशः malloc को आवश्यक आकार आवंटित करने की आवश्यकता नहीं है, जो चीजों को थोड़ा और जटिल बनाता है।

संपादित

दो और आम वाक्यांश: पहला मामला भी स्थिर स्मृति आवंटन (संकलक द्वारा किया जाता है) के रूप में जाना जाता है, दूसरे मामले गतिशील स्मृति आवंटन (क्रम प्रणाली द्वारा किया) को दर्शाता है।

2

और पढ़ें के बारे में dynamic memory allocation और भी garbage collection

आप वास्तव में करने के लिए एक अच्छा सी या C++ प्रोग्रामिंग पुस्तक पढ़ा की जरूरत है।

विस्तार से समझाए जाने में काफी समय लगेगा।

ढेर स्मृति है जिसमें गतिशील आवंटन (सी ++ में new या malloc सी में होता है) होता है। ढेर बढ़ने और घटने के साथ system calls शामिल हैं। लिनक्स पर, वे mmap & munmap हैं (malloc और new आदि लागू करने के लिए उपयोग किए जाते हैं ...)।

आप आवंटन आदिम को कई बार कॉल कर सकते हैं। तो आप एक लूप के अंदर int *p = new int; डाल सकते हैं, और जब भी आप लूप करते हैं तो एक नया स्थान प्राप्त करें!

स्मृति जारी करने के लिए मत भूलना (delete सी ++ या free सी में)। अन्यथा, आपको memory leak -एक शरारती प्रकार की बग मिलेगी। लिनक्स पर, valgrind उन्हें पकड़ने में मदद करता है।

1

संक्षेप में, गतिशील रूप से आवंटित ऑब्जेक्ट जीवनकाल आपके द्वारा पर नियंत्रित होता है और भाषा द्वारा नहीं। यह आपको इसे तब तक रहने की इजाजत देता है जब तक इसकी आवश्यकता होती है (जैसा कि दायरे के अंत के विपरीत होता है), संभवतः ऐसी स्थिति द्वारा निर्धारित किया जाता है जिसे केवल रन-रिम पर गणना की जा सकती है।

इसके अलावा, गतिशील स्मृति आमतौर पर अधिक "स्केलेबल" होती है - यानी आप स्टैक-आधारित आवंटन की तुलना में अधिक और/या बड़ी वस्तुओं को आवंटित कर सकते हैं।

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

4
  1. आपके प्रोग्राम को स्टार्टअप पर स्मृति का प्रारंभिक हिस्सा मिलता है। इस स्मृति को स्टैक कहा जाता है। इन दिनों आमतौर पर राशि लगभग 2 एमबी होती है।

  2. आपका प्रोग्राम ओएस से अतिरिक्त मेमोरी के लिए पूछ सकता है। इसे गतिशील स्मृति आवंटन कहा जाता है। यह मुफ्त स्टोर (सी ++ शब्दावली) या ढेर (सी शब्दावली) पर स्मृति आवंटित करता है। आप उतनी मेमोरी मांग सकते हैं क्योंकि सिस्टम देने के इच्छुक है (एकाधिक गीगाबाइट्स)।

ढेर पर एक चर आवंटित करने के लिए वाक्य रचना इस तरह दिखता है:

{ 
    int a; // allocate on the stack 
} // automatic cleanup on scope exit 

एक चर मुक्त दुकान से स्मृति का उपयोग आवंटित करने के लिए वाक्य रचना इस तरह दिखता है:

int * a = new int; // ask OS memory for storing an int 
delete a; // user is responsible for deleting the object 


अपने सवालों के जवाब देने के लिए:

एक विधि को दूसरे के लिए पसंदीदा कब दिया जाता है।

  1. आम तौर पर स्टैक आवंटन को प्राथमिकता दी जाती है।
  2. डायनामिक आवंटन आवश्यक है जब आपको इसके मूल प्रकार का उपयोग करके एक पॉलीमोर्फिक ऑब्जेक्ट को स्टोर करने की आवश्यकता होती है।
  3. हमेशा विलोपन स्वचालित करने के लिए स्मार्ट सूचक का उपयोग करें:
    • सी ++ 03: boost::scoped_ptr, boost::shared_ptr या std::auto_ptr
    • सी ++ 11: std::unique_ptr या std::shared_ptr

उदाहरण के लिए:

// stack allocation (safe) 
Circle c; 

// heap allocation (unsafe) 
Shape * shape = new Circle; 
delete shape; 

// heap allocation with smart pointers (safe) 
std::unique_ptr<Shape> shape(new Circle); 

इसके अलावा अधिक कारण है कि मैं पहले मामले में स्मृति को मुक्त करने के लिए, लेकिन दूसरे मामले में नहीं की जरूरत है।

जैसा कि मैंने ऊपर बताया है आवंटित आवंटित चर स्वचालित रूप से दायरे से बाहर निकलते हैं। ध्यान दें कि आपको स्टैक मेमोरी को हटाने की अनुमति नहीं है। ऐसा करने से आपके आवेदन को अनिवार्य रूप से दुर्घटनाग्रस्त कर दिया जाएगा।

+0

यह उत्तर दूसरों की तुलना में काफी बेहतर है क्योंकि यह वास्तव में बताता है कि किस स्थिति में किस स्मृति आवंटन का उपयोग करना है –

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