2010-03-11 11 views
12

मैं कुछ विरासत कोड इस तरह एक समारोह में शामिल है कि भर में आया था:क्या यह विरासत कोड नहीं है जो स्थानीय चार सरणी को गलत बनाता है?

LPCTSTR returnString() 
{ 
    char buffer[50000]; 
    LPCTSTR t; 

    /*Some code here that copies some string into buffer*/ 

    t = buffer; 
    return t; 
} 

अब, मैं दृढ़ता से संदेह है कि यह गलत है। मैंने फ़ंक्शन को कॉल करने का प्रयास किया और यह उस स्ट्रिंग को वापस कर देता है जिसे आप वापस लौटने की उम्मीद करते हैं। हालांकि, मैं वास्तव में नहीं देखता कि यह कैसे होता है: char सरणी को स्टैक पर संग्रहीत किया जाना चाहिए, और इस प्रकार फ़ंक्शन से बाहर निकलने के बाद इसे हटा दिया गया है? अगर मैं गलत हूं, और यह ढेर पर संग्रहीत हो जाता है, तो क्या यह कार्य स्मृति रिसाव नहीं बना रहा है?

+5

हाँ, आप सही हैं। आपको किसी फ़ंक्शन से स्थिर सरणी वापस नहीं करनी चाहिए। – AraK

+1

@nvl भले ही 't' गतिशील रूप से आवंटित किया जा रहा हो, फिर भी बफर को सही तरीके से डुप्लिकेट करने के लिए इसे अधिभारित असाइनमेंट ऑपरेटर की आवश्यकता होगी। – KevenK

+2

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

उत्तर

8

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

+3

बेहतर अभी तक, एक 'std :: basic_string ' या इसी तरह की ऑब्जेक्ट को वापस लौटाएं जो जानता है कि किसी भी संसाधन की प्रतिलिपि बनाने और उसे कैसे रोकना है। –

3

आपकी भावना सही है; कोड बहुत गलत है।

स्मृति वास्तव में ढेर पर है, और फ़ंक्शन के अंत से दूर हो जाती है।

यदि बफर स्थिर था तो आप कम से कम एक समय में (एकल-थ्रेडेड ऐप में) एक कॉल के लिए काम करने की उम्मीद कर सकते थे।

5

आप सही हैं, यह काम करने की गारंटी नहीं है; और वास्तव में, यदि आप वास्तव में इस बफर में 50,000 वर्ण स्ट्रिंग को संग्रहीत करते हैं, तो उसके बाद कुछ फ़ंक्शन कॉल करें (जो फ़ंक्शन को कॉल करता है, जो फ़ंक्शन को कॉल करता है ..) इसके बाद, लगभग हर सिस्टम में यह स्ट्रिंग दूषित हो जाएगी, फ़ंक्शन के कारण स्टैक-फ्रेम ढेर पर धकेल दिया जा रहा है।

काम करने के लिए एकमात्र कारण यह है कि फ़ंक्शन रिटर्न के बाद स्टेल स्टैक मेमोरी (अधिकांश सिस्टम पर) साफ़ नहीं होती है।

[संपादित करें] स्पष्ट करने के लिए, ऐसा लगता है कि स्टैक को 50,000 बाइट्स बढ़ने का मौका नहीं मिला है। इसे char buffer[10]; पर बदलने का प्रयास करें और returnString() के बाद कुछ फ़ंक्शंस कॉल करें, और आपको इसे दूषित देखना चाहिए।

1

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

बजाय इस बात का

, आप एक स्थिर बफर का उपयोग कर सकते हैं:

static char buffer[50000]; 

जो ढेर पर आवंटित नहीं किया गया है, तो यह करने के लिए एक सूचक मान्य रहता है। (यह धागा सुरक्षित नहीं है, जाहिर है)।

6

कोई स्मृति रिसाव, लेकिन समारोह अभी भी गलत है - buffer वास्तव में स्टैक पर बनाई गई है, और तथ्य यह है कि फोन करने वाले यह पढ़ने में सक्षम है सिर्फ भाग्य है (यह केवल सही returnString() कार्य करने के लिए कॉल के बाद पहुंचा जा सकता है।) उस बफर को किसी और फ़ंक्शन कॉल या अन्य स्टैक मैनिपुलेशन द्वारा ओवरराइट किया जा सकता है।

कॉल श्रृंखला को डेटा पास करने का उचित तरीका एक बफर और आकार को भरने के लिए एक फ़ंक्शन प्रदान करना है।

1

आप सही हैं, कोड गलत है। :)

आपको सी/सी ++ में स्मृति आवंटन का अध्ययन करना चाहिए। डेटा दो क्षेत्रों में रह सकता है: ढेर और ढेर।स्थानीय चर ढेर में संग्रहीत हैं। malloc एड और new एड डेटा ढेर में संग्रहीत किया जाता है। स्टैक स्टेटस फ़ंक्शन करने के लिए स्थानीय है - वैरिएबल स्टैक फ्रेम में रहते हैं - कंटेनर जो फ़ंक्शन रिटर्न के दौरान मुक्त हो जाएगा। तो पॉइंटर्स टूटा हो जाता है।

हीप वैश्विक है, इसलिए प्रोग्रामर द्वारा स्पष्ट रूप से delet एड या free डी तक सभी डेटा संग्रहीत किया जाता है। आप उस क्षेत्र पर भरोसा कर सकते हैं।

1

जबकि सभी के विचार हैं कि व्यवहार अपरिभाषित है, और इस मामले में यह सच साबित होता है, इस तरह के मामलों में अन्य संभावनाओं पर विचार करना महत्वपूर्ण है।

उदाहरण के लिए, एक ओवरलोडेड operator=(const char*) आवश्यकता स्मृति आवंटित दृश्यों के पीछे हो सकता है। यद्यपि यह माइक्रोसॉफ्ट टाइपिफ के साथ मामला नहीं है (मेरे ज्ञान का सबसे अच्छा), इस तरह के मामलों में जागरूक होना एक महत्वपूर्ण बात है।

हालांकि, इस मामले में, यह केवल सुविधाजनक है कि यह काम कर रहा है, और यह निश्चित रूप से गारंटीकृत व्यवहार नहीं है। जैसा कि अन्य ने ध्यान दिया है, यह वास्तव में तय किया जाना चाहिए।

+2

यह संभव नहीं है - फ़ंक्शन एक char * (एलपीसीआरटी की नींव में) देता है, और आप पॉइंटर्स के लिए ओवरलोड ऑपरेटर =() नहीं कर सकते हैं। –

+1

मुझे पता है कि * इस मामले में * यह नहीं चल रहा है कि क्या हो रहा है। मुद्दा यह था कि इस तरह की स्थितियों में (विशेष रूप से ऑब्जेक्ट असाइनमेंट या कंटेनर क्लासेस के साथ) यह होने पर यह ध्यान में रखना एक महत्वपूर्ण विशेषता है कि क्या हो रहा है। अन्यथा, एक बुरा असाइनमेंट की तरह लग सकता है वास्तव में पूरी तरह से मान्य हो सकता है। मेरा बिंदु पूरी तरह निर्देशित था कि इस तरह की स्थिति की जांच कैसे करें, और इस विशिष्ट मामले में * क्या हो रहा है इसका वर्णन करने का इरादा नहीं था *। – KevenK

2

सी/सी ++ में एक सरणी और पॉइंटर के बीच बहुत कम अंतर है। तो कथन:

 
t = buffer; 

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

1

यह आपके कोड में एक खतरनाक बग है। सी और सी ++ में आपको पर फ़ंक्शन में डेटा को ढेर करने के लिए पॉइंटर वापस करने की अनुमति नहीं है। यह अपरिभाषित व्यवहार में परिणाम देता है। मैं समझाऊंगा क्यों।

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

  • पिछला ढेर
  • पैरामीटर
  • (अन्य डेटा)
  • बफर (50000 बाइट्स)
  • टी (sizeof सूचक)

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

बुला समारोह तो लग रहा है क्या टी को बिंदुओं पर, यह उस यह ढेर है कि या मौजूद नहीं हो सकता है पर स्मृति को इंगित करता है खोजने के लिए जा रहा है।(रनटाइम ने इसे स्टैक से पॉप किया, लेकिन स्टैक पर डेटा संयोग से हो सकता है, शायद नहीं)।

अच्छी खबर यह है कि यह निराशाजनक नहीं है। स्वचालित उपकरण हैं जो आपके सॉफ़्टवेयर में इस प्रकार की त्रुटियों की खोज कर सकते हैं और उन्हें रिपोर्ट कर सकते हैं। उन्हें स्थैतिक विश्लेषण उपकरण कहा जाता है। Sentry एक ऐसे प्रोग्राम का ऐसा उदाहरण है जो इस दोष की रिपोर्ट कर सकता है।

1

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

(हाँ मैं इस लीक पता है, नहीं बिंदु)


class LPCTSTR{ 
private: 
    char * foo; 

public: 
    LPCTSTR & operator=(char * in){ 
    foo = strdup(in); 
    } 

    const char * getFoo(){ 
    return foo; 
    } 


}; 

+0

ठीक है अगर यह था, कोड अभी भी काम नहीं करेगा। कार्यप्रणाली से LPCSTR मान को सही तरीके से कॉपी करने के लिए आपको उपयोगकर्ता द्वारा परिभाषित प्रतिलिपि बनाने की आवश्यकता होगी। –

+1

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

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