2009-03-01 22 views
120

मैं थोड़ी देर के लिए प्रोग्रामिंग कर रहा हूं लेकिन यह ज्यादातर जावा और सी # रहा है। मुझे वास्तव में कभी भी स्मृति का प्रबंधन नहीं करना पड़ा। मैंने हाल ही में सी ++ में प्रोग्रामिंग शुरू की है और मैं थोड़ी उलझन में हूं कि जब मुझे स्टैक पर चीज़ों को स्टोर करना चाहिए और उन्हें ढेर पर कब स्टोर करना चाहिए।सी ++ में उचित ढेर और ढेर उपयोग?

मेरी समझ यह है कि वे चर जो अक्सर उपयोग किए जाते हैं उन्हें ढेर और वस्तुओं पर संग्रहीत किया जाना चाहिए, शायद ही कभी उपयोग किए जाने वाले चर, और बड़े डेटा संरचनाओं को ढेर पर संग्रहीत किया जाना चाहिए। क्या यह सही है या मैं गलत हूँ?

+0

संभावित डुप्लिकेट [ढेर के बजाय ढेर का उपयोग करना सबसे अच्छा कब है और इसके विपरीत?] (Http://stackoverflow.com/questions/102009/when-is-it-best-to-use -थ-स्टैक-के-द-द-हेप-एंड-वाइस-वर्सा) –

उत्तर

238

नहीं, ढेर और ढेर के बीच का अंतर प्रदर्शन नहीं है। यह जीवनकाल है: किसी भी फ़ंक्शन के अंदर कोई स्थानीय चर (जो भी आप malloc() या new नहीं करते हैं) स्टैक पर रहता है। जब आप फ़ंक्शन से वापस आते हैं तो यह दूर हो जाता है। यदि आप इसे घोषित करने वाले फ़ंक्शन से अधिक समय तक जीना चाहते हैं, तो आपको इसे ढेर पर आवंटित करना होगा।

class Thingy; 

Thingy* foo() 
{ 
    int a; // this int lives on the stack 
    Thingy B; // this thingy lives on the stack and will be deleted when we return from foo 
    Thingy *pointerToB = &B; // this points to an address on the stack 
    Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap. 
            // pointerToC contains its address. 

    // this is safe: C lives on the heap and outlives foo(). 
    // Whoever you pass this to must remember to delete it! 
    return pointerToC; 

    // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. 
    // whoever uses this returned pointer will probably cause a crash! 
    return pointerToB; 
} 

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

+6

यह जोड़ना सुरक्षित होगा कि अलग-अलग आकार की जानकारी आम तौर पर ढेर पर जाती है। एकमात्र अपवाद जिन्हें मैं जानता हूं वो वीएलए में सी 99 (जिसमें सीमित समर्थन है) और एलोका() फ़ंक्शन जिसे अक्सर प्रोग्रामर द्वारा भी गलत समझा जाता है। –

+10

अच्छी व्याख्या, हालांकि आवंटित आवंटन और/या विध्वंस के साथ एक बहुप्रचारित परिदृश्य में, ढेर * विवाद का एक बिंदु है, इस प्रकार प्रदर्शन को प्रभावित करता है। फिर भी, गुंजाइश हमेशा निर्णायक कारक है। – peterchen

+17

निश्चित रूप से, और नया/malloc() स्वयं धीमा ऑपरेशन है, और ढेर एक मनमाने ढंग से ढेर लाइन की तुलना में dcache में होने की अधिक संभावना है। ये असली विचार हैं, लेकिन आम तौर पर जीवनकाल के सवाल के लिए माध्यमिक हैं। – Crashworks

1

ढेर या ढेर पर आवंटित करने का विकल्प यह है कि आपके चर के आवंटन के आधार पर आपके लिए बनाया गया है। यदि आप "नया" कॉल का उपयोग करके कुछ गतिशील रूप से आवंटित करते हैं, तो आप ढेर से आवंटित कर रहे हैं। यदि आप किसी वैश्विक चर के रूप में कुछ आवंटित करते हैं, या फ़ंक्शन में पैरामीटर के रूप में इसे स्टैक पर आवंटित किया जाता है।

+3

मुझे संदेह है कि वह कह रहा था कि चीजों को ढेर पर कब रखना है, नहीं। –

6

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

5

अन्य उत्तरों में जोड़ने के लिए, यह कम से कम थोड़ा सा प्रदर्शन के बारे में भी हो सकता है। ऐसा नहीं है कि आपको इसके बारे में चिंता न करें जब तक कि यह आपके लिए प्रासंगिक न हो, लेकिन:

ढेर में आवंटित करने के लिए स्मृति की एक ब्लॉक को ट्रैक करने की आवश्यकता होती है, जो निरंतर समय के संचालन नहीं होता है (और कुछ चक्र और उपरांत लेता है)। यह धीमा हो सकता है क्योंकि स्मृति खंडित हो जाती है, और/या आप अपने पता स्थान का 100% उपयोग करने के करीब आ रहे हैं। दूसरी ओर, ढेर आवंटन स्थिर समय होते हैं, मूल रूप से "मुक्त" संचालन।

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

+0

ढेर और ढेर दोनों वर्चुअल मेमोरी पगे हुए हैं। ढेर खोज समय नई स्मृति में मैप करने के लिए क्या लेता है इसकी तुलना में तेज तेज है। 32 बिट लिनक्स के तहत, मैं अपने स्टैक पर> 2gig डाल सकता हूं। मैक के तहत, मुझे लगता है कि ढेर 65 मेग तक सीमित है। –

39

मैं कहूंगा:

स्टोर ढेर पर, यदि आप कर सकते।

यदि आपको आवश्यकता है, तो इसे ढेर पर स्टोर करें।

इसलिए, ढेर के ढेर को प्राथमिकता दें।कुछ संभावित कारण है कि आप ढेर पर कुछ की दुकान नहीं कर सकते हैं:

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

ढेर पर गैर-निश्चित आकार की वस्तुओं को आवंटित करने के लिए समझदार कंपाइलर्स के साथ यह संभव है (आमतौर पर सरणी जिसका आकार संकलन समय पर ज्ञात नहीं है)।

+0

कुछ केबी से अधिक कुछ आमतौर पर ढेर पर सबसे अच्छा लगाया जाता है। मुझे विशिष्ट जानकारी नहीं है लेकिन मुझे कभी भी एक स्टैक के साथ काम करने की याद नहीं है जो "कुछ मेग्स" था। –

+2

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

+1

दान: मैंने 2 गीगा (हां, जी जीआईजीएस में जी) को 32 बिट लिनक्स के नीचे ढेर पर रखा है। ढेर सीमा ओएस निर्भर हैं। –

0

मेरी राय में वहाँ दो निर्णायक कारक

1) Scope of variable 
2) Performance. 

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

ढेर का उपयोग करते समय प्रदर्शन को बढ़ाने के लिए आप ढेर ब्लॉक बनाने के लिए कार्यक्षमता का भी उपयोग कर सकते हैं और यह विभिन्न चरणीय स्थान में प्रत्येक चर आवंटित करने के बजाय प्रदर्शन प्राप्त करने में मदद कर सकता है।

24

यह अन्य उत्तरों के सुझावों की तुलना में अधिक सूक्ष्म है। ढेर पर डेटा और ढेर पर डेटा के बीच कोई पूर्ण विभाजन नहीं है कि आप इसे कैसे घोषित करते हैं।

std::vector<int> v(10); 

एक समारोह के शरीर, कि ढेर पर दस पूर्णांकों का एक vector (गतिशील सरणी) वाणी में: उदाहरण के लिए। लेकिन vector द्वारा प्रबंधित स्टोरेज स्टैक पर नहीं है।

आह, लेकिन (अन्य उत्तरों सुझाव देते हैं) उस भंडारण का जीवनकाल vector के जीवनकाल से घिरा हुआ है, जो यहां ढेर-आधारित है, इसलिए इससे कोई फर्क नहीं पड़ता कि यह कैसे कार्यान्वित किया जाता है - हम केवल इसका इलाज कर सकते हैं मूल्य semantics के साथ एक ढेर आधारित वस्तु।

ऐसा नहीं है। मान लीजिए फ़ंक्शन था:

void GetSomeNumbers(std::vector<int> &result) 
{ 
    std::vector<int> v(10); 

    // fill v with numbers 

    result.swap(v); 
} 

तो एक swap समारोह (और किसी भी जटिल मान प्रकार एक पर होना चाहिए) एक प्रणाली है जो की एक एकल मालिक की गारंटी देता है के तहत, कुछ ढेर आंकड़ों के rebindable संदर्भ का एक प्रकार के रूप में काम कर सकते हैं के साथ कुछ भी वह डेटा

इसलिए आधुनिक सी ++ दृष्टिकोण नग्न स्थानीय सूचक चर में ढेर डेटा का पता संग्रहीत करता है। सभी ढेर आवंटन कक्षाओं के अंदर छिपा होना चाहिए।

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

a = b; 

इस तरह से उन्हें स्वैप:

a.swap(b); 

आप केवल मदद करने के लिए आप का अनुकूलन ज्ञान से एक विशेष बिट बनाए रखने के लिए है

क्योंकि यह बहुत तेज़ है और यह अपवाद नहीं फेंकता है। एकमात्र आवश्यकता यह है कि आपको उसी मान को बनाए रखने के लिए b की आवश्यकता नहीं है (इसे a के मान प्राप्त करने जा रहे हैं, जो a = b में ट्रैश किया जाएगा)।

नकारात्मकता यह है कि यह दृष्टिकोण आपको वास्तविक वापसी मूल्य के बजाय आउटपुट पैरामीटर के माध्यम से कार्यों से मूल्यों को वापस करने के लिए मजबूर करता है। लेकिन वे rvalue references के साथ सी ++ 0x में फिक्स कर रहे हैं।

सभी की सबसे जटिल परिस्थितियों में, आप इस विचार को सामान्य चरम पर ले जाएंगे और स्मार्ट पॉइंटर क्लास जैसे shared_ptr का उपयोग करेंगे जो पहले से ही tr1 में है। (हालांकि मैं तर्क दूंगा कि यदि आपको इसकी आवश्यकता होती है, तो संभवतः आप संभवतः मानक सी ++ के प्रयोज्यता के मीठे स्थान से बाहर चले गए हैं।)

2

पूर्णता के लिए, आप मिरो समेक के लेख में ढेर का उपयोग करने की समस्याओं के बारे में लेख पढ़ सकते हैं एम्बेडेड सॉफ़्टवेयर का संदर्भ।

A Heap of Problems

3

ढेर और अधिक कुशल, और प्रबंधित scoped डेटा आसान है।

लेकिन ढेर से बड़ा कुछ भी के लिए इस्तेमाल किया जाना चाहिए एक कुछ KB (यह, C++ आसान है सिर्फ ढेर पर एक boost::scoped_ptr बनाने आबंटित स्मृति के लिए सूचक धारण करने के लिए)।

एक रिकर्सिव एल्गोरिदम पर विचार करें जो स्वयं को कॉल करता रहता है। कुल ढेर उपयोग को सीमित करना और अनुमान लगाना बहुत मुश्किल है! जबकि ढेर पर, आवंटक (malloc() या new) NULL या throw आईएनजी लौटकर आउट ऑफ़ मेमोरी का संकेत दे सकता है।

स्रोत: लिनक्स कर्नेल जिसका ढेर 8 केबी से बड़ा नहीं है!

+0

अन्य पाठकों के संदर्भ में: (ए) यहां "चाहिए" पूरी तरह से उपयोगकर्ता की व्यक्तिगत राय है, जो सर्वोत्तम 1 उद्धरण और 1 परिदृश्य से खींचा गया है कि कई उपयोगकर्ताओं को सामना करने की संभावना नहीं है (रिकर्सन)। इसके अलावा, (बी) मानक पुस्तकालय 'std :: unique_ptr' प्रदान करता है, जिसे बूस्ट जैसी किसी भी बाहरी पुस्तकालय को प्राथमिकता दी जानी चाहिए (हालांकि यह समय के साथ मानक को फ़ीड करता है)। –

0

शायद इसका उत्तर बहुत अच्छा रहा है। निम्न स्तर के विवरणों की गहरी समझ रखने के लिए मैं आपको लेखों की निम्न श्रृंखला पर इंगित करना चाहता हूं। एलेक्स डार्बी के पास लेखों की एक श्रृंखला है, जहां वह आपको डीबगर के माध्यम से चलता है। स्टैक के बारे में भाग 3 यहां है। http://www.altdevblogaday.com/2011/12/14/c-c-low-level-curriculum-part-3-the-stack/

+0

लिंक मृत प्रतीत होता है, लेकिन इंटरनेट आर्काइव वेबैक मशीन की जांच से संकेत मिलता है कि यह केवल स्टैक के बारे में बात करता है और इसलिए स्टैक ** _ बनाम_एप ** के विशिष्ट प्रश्न का उत्तर देने के लिए कुछ भी नहीं करता है। -1 –

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