2008-08-04 59 views
11

मैंने सी में फ़ंक्शन को प्रतिस्थापित करने की कोशिश की है, जो char * पर काम करता है, जिसे malloc() का उपयोग करके आवंटित किया गया है। यह थोड़ा अलग है कि यह प्रारंभिक स्ट्रिंग में वर्णों की बजाय तारों को ढूंढ और प्रतिस्थापित करेगा।मुझे realloc() के साथ दोहरी मुक्त त्रुटि क्यों मिल रही है?

यदि खोज और प्रतिस्थापन स्ट्रिंग एक ही लम्बाई है (या प्रतिस्थापन स्ट्रिंग खोज स्ट्रिंग से छोटा है), क्योंकि मेरे पास आवंटित पर्याप्त स्थान है, तो यह करना मुश्किल है। अगर मैं realloc() का उपयोग करने का प्रयास करता हूं, तो मुझे एक त्रुटि मिलती है जो मुझे बताती है कि मैं एक डबल फ्री कर रहा हूं - जो मुझे नहीं लगता कि मैं कैसे हूं, क्योंकि मैं केवल realloc() का उपयोग कर रहा हूं।

शायद एक छोटे से कोड में मदद मिलेगी:

void strrep(char *input, char *search, char *replace) { 
    int searchLen = strlen(search); 
    int replaceLen = strlen(replace); 
    int delta = replaceLen - searchLen; 
    char *find = input; 

    while (find = strstr(find, search)) { 

     if (delta > 0) { 
      realloc(input, strlen(input) + delta); 
      find = strstr(input, search);    
     } 

     memmove(find + replaceLen, find + searchLen, strlen(input) - (find - input)); 
     memmove(find, replace, replaceLen); 
    } 
} 

कार्यक्रम काम करता है, जब तक मैं एक उदाहरण जहां प्रतिस्थापित स्ट्रिंग प्रारंभिक स्ट्रिंग से अधिक समय हो जाएगा में realloc() की कोशिश करो। (यह अभी भी काम करता है, यह सिर्फ त्रुटियों के साथ-साथ परिणाम भी थूकता है)।

यदि यह मदद करता है, बुला कोड लगता है:

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

void strrep(char *input, char *search, char *replace); 

int main(void) { 
    char *input = malloc(81); 

    while ((fgets(input, 81, stdin)) != NULL) { 
     strrep(input, "Noel", "Christmas"); 
    } 
} 

उत्तर

11

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

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

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

में परिणामी:

void strrep(char *input, char *search, char *replace); 
char* strrepm(char *input, char *search, char *replace); 
void strrepmfree(char *input); 
6

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

input = realloc(input, strlen(input) + delta); 
+0

और यदि रीयलॉक विफल रहता है, तो यह न्यूल लौटाता है, और मौजूदा बफर को अकेला छोड़ देता है। आपने अभी पॉइंटर खो दिया है ... :-( –

4

नोट एचटीएमएल मुक्ति कोड से छुटकारा पाने के लिए अपने कोड को संपादित करें।

ठीक है, हालांकि यह कुछ समय हो गया है क्योंकि मैंने सी/सी ++ का उपयोग किया है, फिर भी जब आपके मूल ब्लॉक के बाद मेमोरी में कमरा होता है तो स्मृति सूचक मूल्य का पुन: उपयोग करता है।

उदाहरण के लिए, इस पर विचार करें:

(xxxxxxxxxx ..........)

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

हालांकि, यदि आप बाद में इसे 10 बाइट्स द्वारा विकसित करने का प्रयास करते हैं, और केवल 5 उपलब्ध हैं, तो इसे ब्लॉक में स्मृति को स्थानांतरित करने और अपने सूचक को अपडेट करने की आवश्यकता होगी।

हालांकि, आपके उदाहरण में आप फ़ंक्शन को चरित्र के लिए एक पॉइंटर पास कर रहे हैं, न कि आपके चर के लिए पॉइंटर, और इस प्रकार स्ट्रैप फ़ंक्शन आंतरिक रूप से उपयोग में चर समायोजित करने में सक्षम हो सकता है, यह एक स्थानीय चर है strrep फ़ंक्शन और आपका कॉलिंग कोड मूल पॉइंटर चर वैल्यू के साथ छोड़ा जाएगा।

हालांकि, इस सूचक मूल्य को मुक्त कर दिया गया है।

आपके मामले में, इनपुट अपराधी है।

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

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

0

मेरे त्वरित संकेत।

बजाय:
void strrep(char *input, char *search, char *replace)
कोशिश:
void strrep(char *&input, char *search, char *replace)

और शरीर की तुलना में:
input = realloc(input, strlen(input) + delta);

आम तौर पर के रूप में मूल्यों/संदर्भ और realloc() वर्णन समारोह तर्क गुजर के बारे में पढ़ा:)।

+0

नोटेशन 'शून्य स्ट्रैप (char * और इनपुट, char * search, char * replace)' सी में मान्य नहीं है - हालांकि यह C++ में मान्य है। सवाल यह नहीं है , और AFAICT कभी नहीं था, सी ++ के साथ टैग किया गया। सबसे अच्छा, कोड 'शून्य स्ट्रैप (चार ** इनपुट, चार * खोज, चार * प्रतिस्थापन) होना चाहिए, हालांकि यह तर्क देना आसान है कि' char * strrep (const char * इनपुट, कॉन्स char * search, const char * प्रतिस्थापित करें) 'एक व्यावहारिक इंटरफ़ेस है (इनपुट स्ट्रिंग्स नहीं बदले गए हैं; संशोधित स्ट्रिंग आवंटित और लौटा दी गई है) –

3

ऐसा लगता है कि यह काम करता है;

char *strrep(char *string, const char *search, const char *replace) { 
    char *p = strstr(string, search); 

    if (p) { 
     int occurrence = p - string; 
     int stringlength = strlen(string); 
     int searchlength = strlen(search); 
     int replacelength = strlen(replace); 

     if (replacelength > searchlength) { 
      string = (char *) realloc(string, strlen(string) 
       + replacelength - searchlength + 1); 
     } 

     if (replacelength != searchlength) { 
      memmove(string + occurrence + replacelength, 
         string + occurrence + searchlength, 
         stringlength - occurrence - searchlength + 1); 
     } 

     strncpy(string + occurrence, replace, replacelength); 
    } 

    return string; 
} 

श्वास, क्या चूसने के बिना कोड पोस्ट करने के लिए वैसे भी है?

+0

कोई टिप्पणी जोड़ना, क्योंकि टिप्पणी को उत्तर के रूप में लिखा गया था, टिप्पणी करने से पहले: ऐसा लगता है कि केवल पहली घटना को बदलना है। जो शायद उचित है, क्योंकि मैंने वास्तव में यह नहीं बताया कि यह उन सभी को बदलना चाहिए! –

12

सबसे पहले, क्षमा करें, मैं पार्टी के लिए देर हो चुकी हूं। यह मेरा पहला स्टैक ओवरफ्लो उत्तर है। :)

जैसा कि इंगित किया गया है, जब realloc() कहा जाता है, तो आप पॉइंटर को स्मृति को फिर से बदल सकते हैं। जब ऐसा होता है, तो तर्क "स्ट्रिंग" अमान्य हो जाता है। यहां तक ​​कि यदि आप इसे फिर से सौंप देते हैं, तो फ़ंक्शन समाप्त हो जाने पर परिवर्तन गुंजाइश से बाहर हो जाता है।

ओपी का जवाब देने के लिए, realloc() नव-पुनर्वितरणित स्मृति में एक सूचक देता है। वापसी मूल्य कहीं भी संग्रहीत करने की जरूरत है। आम तौर पर, यदि आप ऐसा करते हैं: TyBoer बताते

data *foo = malloc(SIZE * sizeof(data)); 
data *bar = realloc(foo, NEWSIZE * sizeof(data)); 

/* Test bar for safety before blowing away foo */ 
if (bar != NULL) 
{ 
    foo = bar; 
    bar = NULL; 
} 
else 
{ 
    fprintf(stderr, "Crap. Memory error.\n"); 
    free(foo); 
    exit(-1); 
} 

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

void foobar(char *input, int newlength) 
{ 
    /* Here, I ignore my own advice to save space. Check your return values! */ 
    input = realloc(input, newlength * sizeof(char)); 
} 

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

void foobar(char **input, int newlength) 
{ 
    *input = realloc(*input, newlength * sizeof(char)); 
} 

, अब वह अमान्य हो सकता है:

आप एक डबल सूचक इनपुट के लिए इस तरह इस्तेमाल कर सकते हैं,।

मुझे लगता है कि फ़ंक्शन कॉलर के इनपुट को संशोधित करने का प्रयास करते समय realloc() का उपयोग करने से बचने के लिए सबसे साफ समाधान है। बस malloc() एक नया बफर, इसे वापस करें, और कॉलर को यह तय करने दें कि पुराने पाठ को मुक्त करना है या नहीं। कॉलर को मूल स्ट्रिंग रखने के लिए इसका अतिरिक्त लाभ है!

6

किसी और ने पार्टी के देर से होने के लिए माफ़ी मांगी - ढाई महीने पहले। ओह ठीक है, मैं सॉफ्टवेयर पुरातत्व करने में काफी समय बिताता हूं।

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

विश्लेषण करने से पहले, मैं उन लोगों से सहमत हूं जो कहते हैं कि आपका इंटरफ़ेस तारकीय से कम है; हालांकि, अगर आपने मेमोरी रिसाव/ट्रामप्लिंग मुद्दों के साथ निपटाया है और 'आवंटित स्मृति आवंटित' की आवश्यकता है, तो यह 'ठीक' हो सकता है।

समस्याएं क्या हैं? खैर, आप realloc(), और realloc() को बफर पास करते हैं, आपको उस क्षेत्र में एक नया सूचक देता है जिसका उपयोग आप करना चाहिए - और आप उस वापसी मान को अनदेखा करते हैं। नतीजतन, realloc() शायद मूल स्मृति को मुक्त कर दिया है, और फिर आप इसे एक ही सूचक को फिर से पास कर देते हैं, और यह शिकायत करता है कि आप एक ही स्मृति को दो बार मुक्त कर रहे हैं क्योंकि आप इसे मूल मान फिर से पास करते हैं। यह न केवल स्मृति को रिसाव करता है, बल्कि इसका मतलब है कि आप मूल स्थान का उपयोग जारी रखते हैं - और जॉन डाउनी के अंधेरे बिंदुओं में गोली मार दी गई है कि आप realloc() का दुरुपयोग कर रहे हैं, लेकिन इस बात पर जोर नहीं देते कि आप ऐसा कितना गंभीर कर रहे हैं। एक-एक-एक त्रुटि भी है क्योंकि आप स्ट्रिंग को समाप्त करने वाले NUL '\ 0' के लिए पर्याप्त स्थान आवंटित नहीं करते हैं।

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

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

तो, अपने नामक समारोह के अपने द्वारा सुझाए गए संशोधन है:

char *strrep(char *input, char *search, char *replace) { 
    int searchLen = strlen(search); 
    int replaceLen = strlen(replace); 
    int delta = replaceLen - searchLen; 
    char *find = input; 

    while ((find = strstr(find, search)) != 0) { 
     if (delta > 0) { 
      input = realloc(input, strlen(input) + delta + 1); 
      find = strstr(input, search);    
     } 

     memmove(find + replaceLen, find + searchLen, strlen(input) + 1 - (find - input)); 
     memmove(find, replace, replaceLen); 
    } 

    return(input); 
} 

इस कोड को स्मृति आवंटन त्रुटियों का पता नहीं करता है - और शायद दुर्घटनाओं (लेकिन यदि नहीं, स्मृति लीक) realloc अगर() विफल रहता है। स्मृति प्रबंधन मुद्दों की व्यापक चर्चा के लिए स्टीव Maguire के 'लेखन ठोस कोड' पुस्तक देखें।

+1

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

3

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

मैं कोड को देखा है जहां

realloc(bytes, smallerSize); 

इस्तेमाल किया गया था और बफर आकार बदलने के लिए काम किया है, यह छोटा करने। लगभग दस लाख बार काम किया, फिर किसी कारण से रीयलॉक ने फैसला किया कि भले ही आप बफर को छोटा कर रहे हों, यह आपको एक अच्छी नई प्रतिलिपि देगा। तो बुरी चीजें होने के बाद आप एक यादृच्छिक जगह 1/2 में दुर्घटनाग्रस्त हो जाते हैं।

हमेशा रीयलोक के वापसी मूल्य का उपयोग करें।