Γιατί να πάρει ένα διπλό λάθος ελεύθερο με realloc ();

ψήφοι
11

Έχω προσπαθήσει να γράψει μια σειρά αντικαθιστούν τη λειτουργία στη C, η οποία λειτουργεί σε ένα char *, το οποίο έχει διατεθεί χρήση malloc(). Είναι λίγο διαφορετικό από το γεγονός ότι θα βρείτε και να αντικαταστήσετε χορδές, όχι χαρακτήρες στην αρχική κορδόνι.

Είναι τετριμμένο να κάνετε αν η αναζήτηση και αντικατάσταση χορδές έχουν το ίδιο μήκος (ή το string αντικαταστήσει είναι μικρότερο από το κείμενο αναζήτησης), δεδομένου ότι δεν έχω αρκετό χώρο που διατίθεται. Αν προσπαθήσω να χρησιμοποιήσει 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);
    }
}
Δημοσιεύθηκε 04/08/2008 στις 12:06
πηγή χρήστη
Σε άλλες γλώσσες...                            


8 απαντήσεις

ψήφοι
12

Πρώτα απ 'όλα, συγγνώμη είμαι αργά για το κόμμα. Αυτή είναι η πρώτη απάντηση Stackoverflow μου. :)

Όπως έχει επισημανθεί, όταν realloc () καλείται, μπορείτε να ενδεχομένως να αλλάξει ο δείκτης στη μνήμη να ανακατανεμηθούν. Όταν συμβαίνει αυτό, το επιχείρημα «string» καθίσταται άκυρη. Ακόμα κι αν ανάθεσή της, η αλλαγή βγαίνει από το πεδίο εφαρμογής κάποτε τελειώνει η λειτουργία.

Για να δοθεί απάντηση στο ΕΠ, realloc () επιστρέφει ένα δείκτη προς το νέο-ανακατανομή της μνήμης. Η τιμή επιστροφής πρέπει να αποθηκεύονται κάπου. Σε γενικές γραμμές, θα το κάνετε αυτό:

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);
}

Όπως TyBoer επισημαίνει, εσείς δεν μπορεί να αλλάξει η τιμή του δείκτη να περάσει ως την είσοδο σε αυτή τη λειτουργία. Μπορείτε να ορίσετε ό, τι θέλετε, αλλά η αλλαγή θα βγουν έξω από το πεδίο στο τέλος της λειτουργίας. Στο επόμενο μπλοκ, «είσοδος» μπορεί ή δεν μπορεί να είναι ένα μη έγκυρο δείκτη όταν η λειτουργία συμπληρώνει:

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));
}

Mark προσπαθεί να εργαστούν γύρω από αυτό, επιστρέφοντας το νέο δείκτη ως αποτέλεσμα της συνάρτησης. Αν το κάνετε αυτό, η ευθύνη βαρύνει τον καλούντα να χρησιμοποιήσετε ποτέ ξανά το δείκτη που χρησιμοποιείται για την είσοδο. Αν ταιριάζει με την τιμή επιστροφής, τότε έχετε δύο δείκτες στο ίδιο σημείο και το μόνο που χρειάζεται να καλέσετε δωρεάν () σε ένα από αυτά. Αν δεν ταιριάζουν, ο δείκτης των εισροών επισημαίνει τώρα στη μνήμη που μπορεί ή δεν μπορεί να ανήκει στη διαδικασία. Εύρεση τιμών θα μπορούσε να προκαλέσει ένα σφάλμα κατάτμησης.

Θα μπορούσατε να χρησιμοποιήσετε ένα διπλό δείκτη για την είσοδο, όπως αυτό:

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

Αν ο καλών έχει ένα αντίγραφο του δείκτη εισόδου κάπου, που επαναλαμβάνουν ακόμα θα μπορούσε να είναι άκυρη τώρα.

Νομίζω ότι η πιο καθαρή λύση εδώ είναι να αποφύγετε τη χρήση realloc (), όταν προσπαθεί να τροποποιήσει είσοδο του καλούντος λειτουργία του. Απλά malloc () ένα νέο ρυθμιστικό, επιστρέφουν αυτό, και αφήστε τον καλούντα να αποφασίσουν αν πρέπει ή όχι να απελευθερώσει το παλιό κείμενο. Αυτό έχει το πρόσθετο πλεονέκτημα του να αφήσει τον καλούντα να κρατήσει την αρχική κλωστή!

Απαντήθηκε 08/08/2008 στις 22:37
πηγή χρήστη

ψήφοι
11

Κατά γενικό κανόνα, θα πρέπει ποτέ να κάνετε δωρεάν ή realloc σε ένα buffer χρήστη που παρέχεται. Δεν ξέρετε όπου ο χρήστης έχει χορηγηθεί το χώρο (στην ενότητα σας, σε μια άλλη DLL), έτσι δεν μπορείτε να χρησιμοποιήσετε οποιαδήποτε από τις λειτουργίες κατανομής σε ένα ρυθμιστικό χρήστη.

Υπό την προϋπόθεση ότι τώρα δεν μπορεί να κάνει οποιαδήποτε ανακατανομή μέσα από τη λειτουργία σας, θα πρέπει να αλλάξετε τη συμπεριφορά του λίγο, σαν να κάνει μόνο μία αντικατάσταση, έτσι ώστε ο χρήστης θα είναι σε θέση να υπολογίσει το προκύπτον μήκος συμβολοσειράς max και σας παρέχει ένα ρυθμιστικό αρκετό καιρό για αυτό αντικατάστασης να συμβεί.

Στη συνέχεια, μπορείτε να δημιουργήσετε μια άλλη λειτουργία για να κάνετε τις πολλαπλές αντικαταστάσεις, αλλά θα πρέπει να διαθέσει το σύνολο του χώρου για την προκύπτουσα κλωστή και να αντιγράψετε το string εισόδου του χρήστη. Στη συνέχεια θα πρέπει να παρέχει έναν τρόπο για να διαγράψετε τη φράση που διατίθενται.

Εχοντας ως αποτέλεσμα:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
Απαντήθηκε 04/08/2008 στις 12:19
πηγή χρήστη

ψήφοι
6

Κάποιος άλλος ζήτησε συγγνώμη για την καθυστέρηση στο κόμμα - δυόμισι μήνες πριν. Μα καλά, περνάω πάρα πολύ χρόνο να κάνει την αρχαιολογία λογισμικού.

Ενδιαφέρομαι ότι κανείς δεν έχει σχολιάσει ρητά για τη διαρροή μνήμης στο αρχικό σχέδιο, ή το σφάλμα off-προς-ένα. Και παρατηρούσε τη διαρροή μνήμης που μου λέει ακριβώς γιατί παίρνετε το διπλό χωρίς λάθος (επειδή, για να είμαι ακριβής, θα σας απελευθερώνοντας την ίδια τη μνήμη πολλές φορές - και κάνετε έτσι μετά καταπάτηση πάνω από το ήδη ελευθερωθεί μνήμη).

Πριν από τη διεξαγωγή της ανάλυσης, εγώ θα συμφωνήσω με όσους λένε περιβάλλον σας είναι λιγότερο από αστρική? Ωστόσο, αν αντιμετωπιστεί η διαρροή μνήμης / ποδοπάτημα θέματα και να τεκμηριώνεται η απαίτηση «πρέπει να διατεθεί μνήμης», θα μπορούσε να είναι «ΟΚ».

Ποια είναι τα προβλήματα; Λοιπόν, περνάτε ένα ρυθμιστικό για να realloc (), και realloc () που επιστρέφει ένα νέο δείκτη για την περιοχή θα πρέπει να χρησιμοποιήσετε - και αγνοούν ότι η τιμή επιστροφής. Κατά συνέπεια, realloc () έχει κατά πάσα πιθανότητα απελευθέρωσε την αρχική μνήμη, και στη συνέχεια να περάσει το ίδιο δείκτη ξανά, και καταγγέλλει ότι είστε απελευθερώνοντας την ίδια μνήμη δύο φορές, επειδή έχετε περάσει την αρχική τιμή για να το ξανά. Αυτό όχι μόνο διαρροές μνήμης, αλλά σημαίνει ότι θα συνεχίζουν να χρησιμοποιούν το αρχικό διάστημα - και πυροβόλησε τον John Downey στο σκοτάδι επισημαίνει ότι είστε κατάχρηση realloc (), αλλά δεν τονίσω πόσο σοβαρά θα το κάνουν. Υπάρχει επίσης μια off-από-ένα λάθος γιατί δεν διαθέσουν αρκετό χώρο για τον NUL «\ 0», που τερματίζει το string.

Η διαρροή μνήμης παρουσιάζεται επειδή δεν παρέχει ένα μηχανισμό για να πει τον καλούντα για την τελευταία τιμή του string. Επειδή σας κράτησε καταπάτηση πάνω από την αρχική νήματος συν το χώρο μετά, μοιάζει ο κώδικας λειτούργησε, αλλά αν κωδικό κλήσης σας ελευθερωθεί ο χώρος, το οποίο επίσης θα πάρει ένα διπλό χωρίς λάθος, ή θα μπορούσε να πάρει έναν πυρήνα χωματερή ή ισοδύναμο γιατί η πληροφορία ελέγχου μνήμης είναι πλήρως κωδικοποιημένα.

Ο κωδικός σας επίσης δεν προστατεύει κατά της επ 'αόριστον την ανάπτυξη - να εξετάσει την αντικατάσταση «Noel» με το «Joyeux Noel». Κάθε φορά, που θα προσθέσει 7 χαρακτήρες, αλλά θα βρίσκατε μια άλλη Noel στην αντικατάσταση κειμένου, και να επεκτείνει το, και ούτω καθεξής και ούτω καθεξής. fixup μου (παρακάτω) δεν αντιμετωπίζει το ζήτημα αυτό - η απλή λύση είναι πιθανόν να ελέγξει αν η συμβολοσειρά αναζήτησης εμφανίζεται στη σειρά αντικαταστήσει? μια εναλλακτική λύση είναι να υπερπηδήσει το string αντικαταστήσει και να συνεχίσει την αναζήτηση μετά από αυτό. Το δεύτερο έχει κάποια μη τετριμμένα θέματα κωδικοποίησης για την αντιμετώπιση.

Έτσι, πρότεινε την αναθεώρηση μου που ονομάζεται λειτουργία σας είναι:

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 () αποτυγχάνει. Δείτε «Γράφοντας Στερεά Κώδικας» το βιβλίο του Steve Maguire για μια εκτεταμένη συζήτηση για τα θέματα διαχείρισης μνήμης.

Απαντήθηκε 21/10/2008 στις 03:41
πηγή χρήστη

ψήφοι
6

Ακριβώς ένας πυροβολισμός στο σκοτάδι, γιατί δεν έχω δοκιμάσει ακόμα, αλλά όταν realloc επιστρέφει το δείκτη σαν malloc. Επειδή realloc να μετακινήσετε το δείκτη, αν χρειαστεί είστε πιο πιθανό λειτουργούν σε ένα μη έγκυρο δείκτη, αν δεν κάνετε τα εξής:

input = realloc(input, strlen(input) + delta);
Απαντήθηκε 04/08/2008 στις 12:14
πηγή χρήστη

ψήφοι
4

Σημείωση, προσπαθήστε να αλλάξετε τον κωδικό σας για να απαλλαγούμε από τους κωδικούς HTML διαφυγής.

Λοιπόν, αν και έχει περάσει καιρός από τότε που χρησιμοποιείται C / C ++, realloc που μεγαλώνει επαναχρησιμοποιεί μόνο την αξία δείκτη μνήμη αν υπάρχει χώρος στη μνήμη μετά την αρχική μπλοκ σας.

Για παράδειγμα, σκεφτείτε το εξής:

(Xxxxxxxxxx ..........)

Αν τα σημεία δείκτη σας στα πρώτα χ, και. σημαίνει ελεύθερη θέση μνήμης, και θα αυξηθεί το μέγεθος της μνήμης που υποδεικνύεται από μεταβλητές σας με 5 bytes, αυτό θα πετύχει. Αυτό είναι φυσικά ένα απλοποιημένο παράδειγμα όπως τα μπλοκ στρογγυλοποιούνται σε ένα ορισμένο μέγεθος για την ευθυγράμμιση, αλλά ούτως ή άλλως.

Ωστόσο, εάν στη συνέχεια προσπαθήσετε να το αυξηθούν κατά ένα άλλο 10 bytes, και υπάρχει μόνο 5 διαθέσιμες, θα πρέπει να μετακινήσετε το μπλοκ στη μνήμη και να ενημερώσετε το δείκτη σας.

Ωστόσο, το παράδειγμά σας περνάτε τη λειτουργία ενός δείκτη στο χαρακτήρα, όχι ένα δείκτη σε μεταβλητή σας, και έτσι ενώ η λειτουργία strrep εσωτερικά να είναι σε θέση να ρυθμίσετε τη μεταβλητή που χρησιμοποιείται, είναι μια τοπική μεταβλητή στη συνάρτηση strrep και καλώντας τον κωδικό σας θα πρέπει να μείνει με την αρχική τιμή της μεταβλητής δείκτη.

Η τιμή του δείκτη, ωστόσο, έχει απελευθερωθεί.

Στην περίπτωσή σας, εισόδου είναι ο ένοχος.

Ωστόσο, θα ήθελα να κάνω μια άλλη πρόταση. Στην περίπτωσή σας μοιάζει η είσοδος μεταβλητή είναι πράγματι εισόδου, και αν είναι, δεν θα πρέπει να τροποποιηθεί, σε όλα.

Έτσι θα προσπαθήσει να βρει έναν άλλο τρόπο για να κάνει ό, τι θέλετε να κάνετε, χωρίς να αλλάζει είσοδο , όπως παρενέργειες, όπως αυτό μπορεί να είναι δύσκολο να εντοπίσουμε.

Απαντήθηκε 04/08/2008 στις 12:17
πηγή χρήστη

ψήφοι
3

realloc είναι περίεργο, πολύπλοκο και πρέπει να χρησιμοποιείται μόνο όταν ασχολούνται με πολλά πολλά μνήμης φορές ανά δευτερόλεπτο. δηλαδή - όπου κάνει πραγματικά τον κωδικό σας γρηγορότερα.

Έχω δει κώδικας όπου

realloc(bytes, smallerSize);

χρησιμοποιήθηκε και εργάστηκε για να αλλάξετε το μέγεθος του buffer, καθιστώντας τα μικρότερα. Εργάστηκε περίπου ένα εκατομμύριο φορές, τότε για κάποιο λόγο realloc αποφάσισε ότι, ακόμη και αν είχαν μείωση του ρυθμιστικού, θα σας δώσει ένα ωραίο νέο αντίγραφο. Έτσι θα συντριβή σε μια τυχαία θέση 1/2 δεύτερη μετά συνέβη το κακό πράγματα.

Πάντα να χρησιμοποιείτε την τιμή επιστροφής της realloc.

Απαντήθηκε 16/05/2011 στις 23:57
πηγή χρήστη

ψήφοι
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;
}

Αναστεναγμός, υπάρχει ούτως ή άλλως για να υποβάλετε κώδικα χωρίς να το πιπίλισμα;

Απαντήθηκε 04/08/2008 στις 12:39
πηγή χρήστη

ψήφοι
0

γρήγορη συμβουλές μου.

Αντί:
void strrep(char *input, char *search, char *replace)
δοκιμάστε:
void strrep(char *&input, char *search, char *replace)

και σε σχέση με το σώμα:
input = realloc(input, strlen(input) + delta);

Γενικώς διαβάσει για συνεργαζόμενος ορίσματα της συνάρτησης ως τιμές / αναφοράς και realloc () περιγραφή :).

Απαντήθηκε 04/08/2008 στις 12:20
πηγή χρήστη

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more