web-dev-qa-db-fra.com

Impossible de libérer les pointeurs const en C

Comment puis-je libérer un const char*? J'ai alloué une nouvelle mémoire en utilisant malloc, et quand j'essaie de la libérer, je reçois toujours l'erreur "type de pointeur incompatible"

Le code qui cause cela est quelque chose comme:

char* name="Arnold";
const char* str=(const char*)malloc(strlen(name)+1);

free(str); // error here
58
lego69

Plusieurs personnes ont posté la bonne réponse, mais continuent à la supprimer pour une raison quelconque. Vous devez le convertir en un pointeur non-const; free prend un void*, pas un const void*:

free((char*)str);
74
Michael Mrozek

Votre code est inversé.

Ce:

char* name="Arnold";
const char* str=(const char*)malloc(strlen(name)+1);

Devrait ressembler à ceci:

const char* name="Arnold";
char* str=(char*)malloc(strlen(name)+1);

Le type de stockage const indique au compilateur que vous ne souhaitez pas modifier un bloc de mémoire une fois alloué (de manière dynamique ou statique). Libérer de la mémoire, c'est la modifier. Notez que vous n'avez pas besoin de transtyper la valeur de retour de malloc () }, mais ce n'est qu'un aparté.

Il est peu utile d'allouer dynamiquement de la mémoire (ce que vous faites en fonction de la longueur de name) et d'indiquer au compilateur que vous n'avez pas l'intention de l'utiliser. Notez que using signifie écrire quelque chose puis le libérer (éventuellement) plus tard.

Transtyper vers un type de stockage différent ne résout pas le fait que vous ayez tout d'abord inversé les types de stockage :) Cela déclenche simplement un avertissement, qui tentait de vous dire quelque chose.

Si le code est inversé (comme il se doit), free() fonctionnera comme prévu car vous pouvez en fait modifier la mémoire que vous avez allouée. 

23
Tim Post

Cela n'a aucun sens de mallocer un pointeur sur const, car vous ne pourrez pas modifier son contenu (sans hacks laids).

FWIW cependant, gcc donne juste un avertissement pour ce qui suit:

//
// const.c
//

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

int main(void)
{
    const char *p = malloc(100);

    free(p);
    return 0;
}

$ gcc -Wall const.c -o const
const.c: In function ‘main’:
const.c:8: warning: passing argument 1 of ‘free’ discards qualifiers from pointer target type
$ 

Quel compilateur utilisez-vous ?

6
Paul R

Dans certains cas, vous souhaitez libérer un const*. Cependant, vous ne voulez pas le faire sauf si vous l'allouez/l'assignez dans la même fonction. Sinon, vous risquez de casser des choses. Voir le code ci-dessous pour un exemple du monde réel. J'utilise const dans les déclarations de fonction pour montrer que je ne change pas le contenu des arguments. Cependant, c'est réaffecté avec un doublon en minuscule (strdup) qu'il faut libérer.

char* tolowerstring(const char *to_lower)
{
    char* workstring = strdup(to_lower);
    for(;workstring != '\0'; workstring++)
        *workstring = tolower(workstring);
    return workstring;
}

int extension_checker(const char* extension, const char* to_check)
{
    char* tail = tolowerstring(to_check);
    extension = tolowerstring(extension);

    while ( (tail = strstr( tail+1, extension)) ) { /* The +1 prevents infinite loop on multiple matches */
        if ( (*extension != '.' ) && ( tail[-1] != '.'))
            continue;
        if ( tail[strlen(extension)] == '\0') {
            free(tail);
            free( (char*) extension);
            return 1;
        }
    }
    free(tail);
    free( (char *) extension);
    return 0;
}
4
nlstd

Il est inutile de lancer un pointeur malloc'd sur const. Toute fonction utilisant un pointeur const ne devrait pas être responsable de la libération de la mémoire qui lui a été transmise.

3
Puppy

Plusieurs réponses suggèrent simplement de transtyper char*. Mais comme el.pescado a écrit ci-dessus, 

transtyper const en non -const est un symptôme de l'odeur de code.

Il y a des avertissements du compilateur qui protègent contre cela, tels que -Wcast-qual dans gcc, que je trouve très utiles. Si vous avez vraiment avez un cas valide pour libérer un pointeur const (contrairement à ce que beaucoup ont écrit ici, il sont des cas valides, comme l'a souligné nlstd), vous pouvez définir une macro à cette fin comme ce:

#define free_const(x) free((void*)(long)(x))

Cela fonctionne au moins pour gcc. La double distribution fait que la logique -Wcast-qual ne détecte pas ceci comme "renvoyant le const". Inutile de dire que cette macro doit être utilisée avec précaution. En réalité, il ne devrait être utilisé que pour les pointeurs affectés à la même fonction.

1
uncleremus

Je peux me tromper mais je pense que le problème réside dans const. Jeter le pointeur sur non-const comme ceci:

free((char *) p);

Parce qu'avec const vous dites: Ne changez pas les données pointées par ce pointeur .

1
Felix Kling

Si vous parlez de C pur et que vous maîtrisez parfaitement l'allocation de mémoire, vous pouvez utiliser l'astuce suivante pour transtyper (const char *) en (char *), ce qui ne vous avertira pas du compilateur:

const char *const_str = (const char *)malloc(...);
char *str = NULL;

union {
  char *mutable_field_p;
  const char *const_field_p;
} u;

u.const_field_p = const_str;
str = u.mutable_field_p;

Maintenant, vous pouvez utiliser free (str); libérer la mémoire.

Mais ATTENTION, ceci est un mal au-delà des mots et ne doit être utilisé que dans un environnement strictement contrôlé (par exemple, une bibliothèque qui alloue et libère des chaînes, mais ne veut pas permettre à l'utilisateur de les modifier). Compilez le temps "STRING" à votre fonction libre.

0
MMasterSK

Je pense que la vraie réponse est que free devrait prendre un argument de pointeur const et que NULL devrait être défini comme un pointeur const. Cela semble être un bug dans les normes. La libération d'un pointeur const doit être implémentée comme suit:

free(p);
p = NULL;

Je ne vois pas comment un compilateur pourrait générer un code incorrect dans ce cas, le pointeur constp n'est plus accessible, aussi importe peu si l'objet qu'il a désigné est const, valide ou autre. Sa const donc il ne peut y avoir aucune copie sale dans les registres ou ailleurs. Il est correct de définir un pointeur const sur une autre valeur, et le fait que cette valeur soit NULL n'a pas d'importance, car la valeur précédente n'est plus accessible.

0
free

Vous ne pouvez pas libérer const char * car il s'agit de const. Stockez les pointeurs reçus de malloc dans des variables de pointeur non const, afin de pouvoir les transmettre à free. Vous pouvez passer des arguments char * à des fonctions prenant des arguments const char *, mais l'inverse n'est pas toujours vrai.

void foo (const char *x);
char *ptr = malloc (...);
foo (ptr);
free (ptr);
0
el.pescado