2009-07-15 6 views
10

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

लेकिन मुझे एक बार पढ़ना याद है कि एक स्ट्रिंग को सीधे रिसाव करने का कोई तरीका है (बिना किसी ऑब्जेक्ट में इसे शामिल किए बिना)। ऐसा लगता है कि संदर्भ के अनुसार एक स्ट्रिंग को पार करने के साथ ऐसा करने के लिए कुछ किया गया था और उसके बाद इसे नियमित रूप से एक बड़े दायरे से एक्सेस किया गया था। हाँ, मुझे पता है कि अस्पष्ट है, यही कारण है कि मैं यहां सवाल पूछ रहा हूं।

उत्तर

2
असल

, में संदर्भ गिनती की अवधि में CONST या गैर स्थिरांक के रूप में स्ट्रिंग गुजर ही कर रहे हैं एक Initialize और Finalize कॉल करने के लिए है डेल्फी 2007 और 200 9। एक ऐसा मामला था जिससे स्ट्रिंग को कॉन्स्ट के रूप में पारित किया जाता है। यहाँ समस्या एक

type 
    TFoo = class 
    S: string; 
    procedure Foo(const S1: string); 
    end; 

procedure TFoo.Foo(const S1: string); 
begin 
    S:= S1; //access violation 
end; 

var 
    F: TFoo; 
begin 
    F:= TFoo.create; 
    try 
    F.S := 'S'; 
    F.Foo(F.S); 
    finally 
    F.Free; 
    end; 
end. 
+0

ऐसा लगता है कि मैं सोच रहा था, लेकिन यह 200 9 में एवी नहीं है, जो मुझे विश्वास है कि आप क्या कह रहे थे। –

+3

यह एवी नहीं है क्योंकि डेल्फी 200 9 में $ constCHECKS चालू होने पर "कॉन्स्ट" इसकी कार्यक्षमता खो देता है। –

7

मुझे आपके दूसरे अनुच्छेद में इस मुद्दे के बारे में पता नहीं है, लेकिन मुझे रिकॉर्ड में लीक तारों द्वारा एक बार काटा गया था।

यदि आप एक रिकार्ड है कि तार आप रेफरी की संख्या और शून्य के साथ गतिशील रूप आबंटित स्मृति के पते के ऊपर लिख शामिल पर FillChar() कहते हैं। जब तक स्ट्रिंग खाली न हो, यह स्मृति को रिसाव कर देगा। इसके आसपास का तरीका को कॉल करना है() रिकॉर्ड पर यह याद रखने से पहले रिकॉर्ड पर है।

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

संकलक संकेत शायद ऐसी अच्छी बात नहीं है, को चुपचाप छोड़कर() कॉल करें यदि इसकी आवश्यकता नहीं है तो बेहतर आईएमएचओ होगा।

+0

> अगर इसे जरूरी नहीं है तो अंतिम रूप से अंतिम() कॉल को छोड़कर आईएमएचओ +1 –

+0

क्या आपको पता है कि इससे कम स्ट्रिंग प्रभावित हैं? –

+0

@JamesB: नहीं, शॉर्ट स्ट्रिंग इस से प्रभावित नहीं होती है, क्योंकि इन्हें संदर्भित नहीं किया जाता है और इसमें स्मृति की मात्रा में पॉइंटर्स शामिल नहीं होते हैं जो खो सकते हैं। लघु तारों में एक लम्बाई बाइट और वर्ण डेटा होता है, इसलिए उन्हें '0' के साथ भरना केवल लंबाई को 0 0 पर सेट करता है और सभी तत्वों को '0 0' पर सेट करता है। – mghie

4

नहीं, मुझे नहीं लगता कि ऐसी चीज हो सकती है। स्ट्रिंग वैरिएबल के लिए एक ऐसा मान प्राप्त करना संभव है जिसकी आपने अपेक्षा नहीं की थी, लेकिन यह स्मृति को रिसाव नहीं करेगा। इस पर विचार करें:

var 
    Global: string; 

procedure One(const Arg: string); 
begin 
    Global := ''; 

    // Oops. This is an invalid reference now. Arg points to 
    // what Global used to refer to, which isn't there anymore. 
    writeln(Arg); 
end; 

procedure Two; 
begin 
    Global := 'foo'; 
    UniqueString(Global); 
    One(Global); 
    Assert(Global = 'foo', 'Uh-oh. The argument isn''t really const?'); 
end; 

यहाँ One तर्क स्थिरांक घोषित किया जाता है, तो माना जाता है कि, यह नहीं बदलेगा। लेकिन फिर One circumvents कि औपचारिक पैरामीटर के बजाय वास्तविक पैरामीटर को बदलकर। प्रक्रिया Two "जानता है" कि One का तर्क स्थिर है, इसलिए यह वास्तविक पैरामीटर को अपने मूल मान को बनाए रखने की अपेक्षा करता है। दावा विफल रहता है।

स्ट्रिंग लीक नहीं हुई है, लेकिन यह कोड दर्शाता है कि आप एक स्ट्रिंग के लिए संदर्भ को कैसे हल कर सकते हैं। ArgGlobal का स्थानीय उपनाम है। यद्यपि हमने Global बदल दिया है, Arg का मान छूटा हुआ है, और क्योंकि इसे कॉन्स्ट घोषित किया गया था, इसलिए स्ट्रिंग की संदर्भ संख्या फ़ंक्शन में प्रवेश पर वृद्धि नहीं हुई थी। Global को पुन: असाइन करना संदर्भ संख्या को शून्य पर गिरा दिया गया, और स्ट्रिंग नष्ट हो गई। Var के रूप में Arg घोषित करना एक ही समस्या होगी; मूल्य से गुजरने से यह समस्या ठीक हो जाएगी। (UniqueString पर कॉल यह सुनिश्चित करने के लिए है कि स्ट्रिंग संदर्भ-गणना की गई है। अन्यथा, यह एक गैर-संदर्भ-गिनती स्ट्रिंग अक्षर हो सकता है।) सभी कंपाइलर-प्रबंधित प्रकार इस समस्या के लिए अतिसंवेदनशील हैं; सरल प्रकार प्रतिरक्षा हैं।

स्ट्रिंग को रिसाव करने का एकमात्र तरीका यह है कि इसे स्ट्रिंग के अलावा किसी अन्य चीज़ के रूप में या गैर-प्रकार-जागरूक स्मृति-प्रबंधन कार्यों का उपयोग करना है।Mghie's answer वर्णन करता है कि एक स्ट्रिंग वैरिएबल को क्लॉबर करने के लिए FillChar का उपयोग कर स्ट्रिंग के अलावा किसी अन्य स्ट्रिंग का इलाज कैसे करें। गैर-प्रकार-जागरूक स्मृति कार्यों में GetMem और FreeMem शामिल हैं। उदाहरण के लिए:

type 
    PRec = ^TRec; 
    TRec = record 
    field: string; 
    end; 

var 
    Rec: PRec; 
begin 
    GetMem(Rec, SizeOf(Rec^)); 
    // Oops. Rec^ is uninitialized. This assignment isn't safe. 
    Rec^.field := IntToStr(4); 
    // Even if the assignment were OK, FreeMem would leak the string. 
    FreeMem(Rec); 
end; 

इसे ठीक करने के दो तरीके हैं।

GetMem(Rec, SizeOf(Rec^)); 
Initialize(Rec^); 
Rec^.field := IntToStr(4); 
Finalize(Rec^); 
FreeMem(Rec); 

अन्य प्रकार अवगत कार्यों का उपयोग करने के लिए है:

New(Rec); 
Rec^.field := IntToStr(4); 
Dispose(Rec); 
+1

वैश्विक उदाहरण किसी भी प्रकार के चर के लिए काम करता है, इसलिए शायद यह नहीं है कि जिम के बाद क्या है। –

+1

आह, आप सही हैं। लेकिन एक स्ट्रिंग के रूप में, यह * अतिरिक्त समस्याएं पैदा कर सकता है, क्योंकि जब मैं इस उत्तर को संपादित करता हूं तो मैं प्रदर्शित करने वाला हूं। –

0

मुझे लगता है कि मैं क्या this की सोच रहा था के समान हो सकता है है।

var 
    p : ^String; 

procedure InitString; 
var 
    s, x : String; 
begin 
    s := 'A cool string!'; 
    x := s + '. Append something to make a copy in' + 
      'memory and generate a new string.'; 

    p := @x; 
end; 

begin 
    { Call a function that will generate a string } 
    InitString(); 

    { Write the value of the string (pointed to by p) } 
    WriteLn(p^); // Runtime error 105! 


    { Wait for a key press } 
    ReadLn; 
end. 
+3

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

+2

लॉरेन, आप * आपके द्वारा लिखे गए वास्तविक कोड की परवाह किए बिना, जैसा कि आप चाहते थे, ठीक उसी तरह काम करने के लिए * उम्मीद करें! कंपाइलर आजकल मानसिक है, है ना? –

+0

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

0

एक और तरीका है एक स्ट्रिंग लीक करने के लिए एक threadvar चर के रूप में यह घोषणा करने के लिए है: यह एक स्ट्रिंग रिसाव के विपरीत, एक स्ट्रिंग जल्दी एकत्र हो जाता है कि है। विवरण के लिए my question देखें। और समाधान के लिए, the solution on how to tidy it देखें।