Pourquoi suis-je recevoir une double erreur libre avec realloc ()?

voix
11

J'ai essayé d'écrire une chaîne remplacer la fonction en C, qui fonctionne sur un char *, qui a été alloué à l' aide malloc(). Il est un peu différent en ce qu'il trouvera et remplacer des chaînes, plutôt que des caractères dans la chaîne de départ.

Il est trivial de le faire si la recherche et remplacer les chaînes sont de la même longueur (ou la chaîne de remplacement est plus courte que la chaîne de recherche), puisque j'ai assez d' espace alloué. Si j'essaie d'utiliser realloc(), je reçois une erreur qui me dit que je suis en train de faire un double libre - que je ne vois pas comment je suis, puisque je ne l' utilise realloc().

Peut-être un peu de code vous aidera:

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

Le programme fonctionne, jusqu'à ce que j'essaie realloc()dans un cas où la chaîne remplacée sera plus longue que la chaîne initiale. (Il reste genre de travaux, juste recrache erreurs, ainsi que le résultat).

Si elle aide, le code d'appel ressemble à:

#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);
    }
}
Créé 04/08/2008 à 12:06
source utilisateur
Dans d'autres langues...                            


8 réponses

voix
12

Tout d'abord, désolé, je suis en retard à la fête. Ceci est ma première réponse stackoverflow. :)

Comme on l'a souligné, lors de realloc () est appelée, vous pouvez potentiellement changer le pointeur sur la mémoire étant réaffecté. Lorsque cela se produit, l'argument « string » devient invalide. Même si vous le réattribuer, le changement est hors de portée une fois que la fonction se termine.

Pour répondre à l'OP, realloc () renvoie un pointeur sur la mémoire nouvellement réaffectés. La valeur de retour doit être stocké quelque part. En règle générale, vous feriez ceci:

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

Comme le souligne TyBoer, vous les gars ne peuvent pas changer la valeur du pointeur étant transmis comme l'entrée à cette fonction. Vous pouvez attribuer tout ce que vous voulez, mais le changement sont hors de portée à la fin de la fonction. Dans le bloc suivant, « entrée » peut ou ne peut pas être un pointeur non valide une fois la fonction complète:

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 essaie de contourner ce problème en retournant le nouveau pointeur comme la sortie de la fonction. Si vous faites cela, il incombe à l'appelant de ne jamais utiliser à nouveau le pointeur qu'il a utilisé pour l'entrée. Si elle correspond à la valeur de retour, alors vous avez deux pointeurs au même endroit et seulement besoin d'appeler gratuitement () sur l'un d'entre eux. Si elles ne correspondent pas, le pointeur d'entrée pointe maintenant à la mémoire qui peuvent ou peuvent ne pas être la propriété du processus. Déréférencement cela pourrait provoquer une erreur de segmentation.

Vous pouvez utiliser un double pointeur pour l'entrée, comme ceci:

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

Si l'appelant a un double du pointeur d'entrée quelque part, qui font double emploi pourrait encore être invalide maintenant.

Je pense que la solution la plus propre est ici pour éviter d'utiliser realloc () lorsque vous essayez de modifier l'entrée de l'appelant de la fonction. Juste malloc () un nouveau tampon, le retour que, et laisser l'appelant décider de libérer l'ancien texte ou non. Cela a l'avantage de laisser l'appelant à garder la chaîne d'origine!

Créé 08/08/2008 à 22:37
source utilisateur

voix
11

En règle générale, vous devriez jamais faire un libre ou realloc sur un tampon fourni par l'utilisateur. Vous ne savez pas où l'utilisateur a alloué l'espace (dans votre module, dans une autre DLL) de sorte que vous ne pouvez pas utiliser l' une des fonctions d'allocation d'un tampon utilisateur.

Pourvu que vous ne pouvez maintenant faire une réaffectation au sein de votre fonction, vous devez changer son comportement un peu comme faire un seul remplacement, l'utilisateur sera en mesure de calculer la chaîne résultante longueur max et vous fournir un tampon assez longtemps pour que celui-ci le remplacement de se produire.

Ensuite, vous pouvez créer une autre fonction pour faire les remplacements multiples, mais vous devrez allouer tout l'espace pour la chaîne résultante et copier la chaîne d'entrée utilisateur. Ensuite, vous devez fournir un moyen de supprimer la chaîne que vous avez alloué.

Résultant en:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
Créé 04/08/2008 à 12:19
source utilisateur

voix
6

Quelqu'un d'autre a présenté ses excuses pour être en retard à la partie - il y a deux mois et demi. Eh bien, je passe beaucoup de temps à faire l'archéologie du logiciel.

Je suis intéressé que personne n'a commenté explicitement sur la fuite de mémoire dans la conception d'origine, ou l'erreur hors par un. Et il observait la fuite de mémoire qui me dit exactement pourquoi vous obtenez l'erreur double libre (car, pour être précis, vous libérez la même mémoire plusieurs fois - et vous le faites après piétiner la mémoire déjà libérée).

Avant de procéder à l'analyse, je suis d'accord avec ceux qui disent l'interface est moins que stellaire; Toutefois, si vous avez traité la fuite de mémoire / questions qui piétinent et documenté l'exigence « doit être la mémoire allouée », il pourrait être « OK ».

Quels sont les problèmes? Eh bien, vous passez un tampon à réallouer () et realloc () vous renvoie un nouveau pointeur vers la zone que vous devez utiliser - et vous ignorez que la valeur de retour. Par conséquent, realloc () a probablement libéré la mémoire d'origine, puis vous transmettre le même pointeur à nouveau, et il se plaint que vous libérant la même mémoire deux fois parce que vous passez la valeur d'origine à nouveau. Cela permet non seulement des fuites de mémoire, mais signifie que vous continuez à utiliser l'espace d'origine - et le tir de John Downey dans les points sombres que vous abusez realloc (), mais ne met pas l'accent la sévérité que vous le faites. Il y a aussi une erreur hors par un parce que vous n'allouent pas assez d'espace pour le NUL « \ 0 » qui met fin à la chaîne.

La fuite de mémoire se produit parce que vous ne fournissez pas un mécanisme pour dire l'appelant à la dernière valeur de la chaîne. Parce que vous avez gardé piétiner la chaîne d'origine, plus l'espace après, il semble que le code a fonctionné, mais si votre code d'appel a libéré l'espace, il serait trop obtenir une erreur double-free, ou il pourrait obtenir une décharge de base ou équivalent parce que les informations de commande de mémoire est complètement brouillé.

Votre code ne protège pas contre la croissance indéfinie - envisager de remplacer « Noel » par « Joyeux Noel ». Chaque fois, vous devez ajouter 7 caractères, mais vous pouvez trouver un autre Noël dans le texte remplacé, et le développer, et ainsi de suite et ainsi de suite. Mon fixup (ci-dessous) ne traite pas de cette question - la solution simple est probablement de vérifier si la chaîne de recherche apparaît dans la chaîne de remplacement; une alternative est de sauter sur la chaîne de remplacement et continuer la recherche après. Le second a quelques problèmes de codage non triviales pour répondre.

Donc, ma révision de votre fonction suggérée est appelée:

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

Ce code ne détecte pas les erreurs d'allocation de mémoire - et se bloque probablement (mais sinon, fuites de mémoire) si realloc () échoue. Voir livre « L'écriture de Solid Code » de Steve Maguire pour une discussion approfondie sur les questions de gestion de la mémoire.

Créé 21/10/2008 à 03:41
source utilisateur

voix
6

Juste un coup de feu dans l'obscurité parce que je n'ai pas encore essayé mais quand vous Realloc il retourne le pointeur tout comme malloc. Parce que realloc peut déplacer le pointeur si nécessaire vous utilisez le plus probable sur un pointeur non valide si vous ne procédez comme suit:

input = realloc(input, strlen(input) + delta);
Créé 04/08/2008 à 12:14
source utilisateur

voix
4

Remarque, essayez de modifier votre code pour se débarrasser des codes d'échappement html.

Eh bien, même si elle a été un moment que je C / C ++, realloc qui ne pousse que réutilise la valeur du pointeur de la mémoire s'il y a de la place en mémoire après votre bloc d'origine.

Par exemple, considérez ceci:

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

Si vos points de pointeur vers le premier x, et. signifie l'emplacement de mémoire libre, et vous développer la taille de la mémoire pointée par votre variable de 5 octets, il réussira. Ceci est bien sûr un exemple simplifié sous forme de blocs sont arrondis à une certaine taille pour l'alignement, mais de toute façon.

Toutefois, si vous essayez ensuite de croître par un autre 10 octets, et il n'y a que 5 disponible, il devra déplacer le bloc en mémoire et mettre à jour votre pointeur.

Cependant, dans votre exemple, vous passez la fonction d'un pointeur sur le caractère, pas un pointeur à votre variable, et donc alors que la fonction strrep peut-être en interne capable d'ajuster la variable en cours d'utilisation, il est une variable locale à la fonction strrep et votre code d'appel sera laissé à la valeur de la variable de pointeur d'origine.

Cette valeur de pointeur, cependant, a été libéré.

Dans votre cas, l'entrée est le coupable.

Cependant, je voudrais faire une autre suggestion. Dans votre cas , il ressemble à l' entrée variable est en effet entrée, et si elle est, il ne devrait pas être modifié, du tout.

Je voudrais essayer donc de trouver une autre façon de faire ce que vous voulez faire, sans changer d' entrée , comme des effets secondaires comme cela peut être difficile à trouver .

Créé 04/08/2008 à 12:17
source utilisateur

voix
3

realloc est étrange, complexe et ne doit être utilisé pour traiter avec beaucoup de lots de mémoire de fois par seconde. à savoir - où il est en fait plus vite votre code.

J'ai vu code où

realloc(bytes, smallerSize);

a été utilisé et a travaillé pour redimensionner le tampon, ce qui en fait plus petit. A travaillé environ un million de fois, puis pour une raison realloc a décidé que même si vous raccourcissement de la mémoire tampon, il vous donnera une belle nouvelle copie. Donc, vous avez un accident dans un endroit aléatoire 1/2 seconde après les mauvaises choses est arrivé.

Toujours utiliser la valeur de retour de realloc.

Créé 16/05/2011 à 23:57
source utilisateur

voix
3

Cela semble fonctionner;

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

Soupir, est de toute façon d'afficher le code sans lui sucer?

Créé 04/08/2008 à 12:39
source utilisateur

voix
0

Mes conseils rapides.

Au lieu de:
void strrep(char *input, char *search, char *replace)
essayer:
void strrep(char *&input, char *search, char *replace)

et que dans le corps:
input = realloc(input, strlen(input) + delta);

Généralement lire sur le passage d'arguments de fonction en tant que valeurs de référence / et :) description realloc ().

Créé 04/08/2008 à 12:20
source utilisateur

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