No se pueden liberar punteros const en C

12 minutos de lectura

avatar de usuario
lego69

¿Cómo puedo liberar un const char*? Asigné nueva memoria usando mallocy cuando intento liberarlo siempre recibo el error “tipo de puntero incompatible”

El código que causa esto es algo como:

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

free(str); // error here

  • ¿Hay alguna razón en particular por la que necesites hacerlo de esta manera? Por lo general, las constantes de cadena se conocen con anticipación, por lo que asignar espacio dinámicamente para una constante de cadena parece extraño.

    –James Kingsbery

    12 de mayo de 2010 a las 14:20

  • Básicamente un problema de lenguaje C. La firma de free() debería haber sido void free(const void* p);. Corregido en C++ (con delete)

    – MSalters

    12 de mayo de 2010 a las 14:26

  • @James Kingsbery: cadenas internas, tal vez: una vez que haya llenado su búfer de caracteres inicialmente, tiene sentido tratarlo a partir de entonces como un const char*. ¿Los que preguntan realmente necesitan nuestro permiso para tener problemas? 😉

    –Steve Jessop

    12 de mayo de 2010 a las 15:16


  • Sin embargo, no tiene ningún sentido en absoluto. Una vez que la memoria se asigna a stres imposible cambiarlo strlo que significa que es permanentemente lo que estaba en la memoria cuando malloc() lo agarró No es posible copiar el nombre sin enviar str. (Además, asignar un literal de cadena a un char * no es bueno, ya que tratar de modificar un literal de cadena es un comportamiento indefinido. Creo que acabas de recibir tu constestá mezclado.)

    –David Thornley

    12 mayo 2010 a las 15:32

  • @DavidThornley: El const char * es posible que se haya convertido de char * después de llenar el contenido; por ejemplo, de const char* foo() { char* s = malloc(...); strcpy(s, ...); return s; }.

    – musiphil

    7 de marzo de 2014 a las 1:06

Varias personas han publicado la respuesta correcta, pero siguen eliminándola por alguna razón. Debe convertirlo en un puntero que no sea constante; free toma una void*No un const void*:

free((char*)str);

  • Funcionará, pero casting const a non-const es un síntoma de olor a código.

    – el.pescado – нет войне

    12 de mayo de 2010 a las 14:17

  • @el. free() es algo así como una excepción, porque es posible que no desee que el puntero se modifique durante su vida útil, pero aún desea liberarlo al final

    – Michael Mrozek

    12 de mayo de 2010 a las 14:20

  • ¿Por qué el elenco a char*? ¿Por qué no directamente gratis ((void *) str)?

    – filante

    12 mayo 2010 a las 14:30

  • Recuerdo haber leído sobre una función de desasignación de memoria en el kernel de Linux que tomó un puntero const, y alguien le preguntó a Linus por qué, y él lo defendió diciendo que en realidad no modifica el valor señalado, ya sea conceptualmente o en la práctica, simplemente parece sube el bloque de memoria usando el puntero y lo desasigna. Estoy de acuerdo con su evaluación y, por lo tanto, considero incorrecta la especificación de la función free(). Pero, por desgracia, es el estándar.

    – rmeador

    12 de mayo de 2010 a las 15:48

  • Si “liberar” estaba cambiando conceptualmente, ¿está bien declarar un const int y luego dejar el ámbito en el que fue declarado? Eso “libera” la variable automática, en el sentido de liberar el recurso y hacer que los punteros a él ya no sean válidos. Es solo un capricho que free toma non-const, no es un mandamiento de lo alto. En el raro caso de que solo haya una cosa que hagas con tu puntero que no sea constante, y sea gratis, entonces pragmáticamente probablemente obtengas más beneficios de un puntero constante (que lanzas gratis) que un puntero no constante (que puede modificar accidentalmente).

    –Steve Jessop

    12 mayo 2010 a las 19:31

avatar de usuario
mensaje de tim

Su código está invertido.

Esta:

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

Debería verse así:

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

los const El tipo de almacenamiento le dice al compilador que no tiene la intención de modificar un bloque de memoria una vez asignado (dinámica o estáticamente). Liberar memoria es modificarla. Tenga en cuenta que no necesita emitir el valor de retorno de malloc(), pero eso es solo un aparte.

No sirve de mucho asignar memoria dinámicamente (lo que está haciendo, en función de la longitud de name) y decirle al compilador que no tiene intención de usarlo. Nota, utilizando lo que significa escribir algo en él y luego (opcionalmente) liberarlo más tarde.

La transmisión a un tipo de almacenamiento diferente no soluciona el hecho de que invirtió los tipos de almacenamiento para empezar 🙂 Solo hace que desaparezca una advertencia, que intentaba decirle algo.

Si el código está invertido (como debería ser), free() funcionará como se esperaba ya que en realidad puede modificar la memoria que asignó.

  • El OP preguntó cómo liberar un puntero a un tipo calificado const; el ejemplo de código adjunto refleja su pregunta, donde su interpretación lo contradice. Aparte, el calificador const en el tipo apuntado no afecta ni expresa ninguna intención en cuanto a lo que se hará a/con un objeto asignado en sí mismo, solo afecta lo que se hará a través de este puntero. Una vez/si descarta el calificador const apuntado, puede modificar el objeto asignado.

    – Dror K.

    3 de marzo de 2017 a las 22:38

  • @DrorK. sin embargo, esta es la respuesta más útil, al menos para mí, ya que cometí el mismo error que el OP. La mayoría de las personas que se encuentran con este problema probablemente estén igualmente confundidas, por lo que planteo que esta es en realidad la mejor respuesta.

    – Michael Dorst

    12 abr 2020 a las 21:04

avatar de usuario
pablo r

No tiene sentido malloc un puntero a const, ya que no podrá modificar su contenido (sin trucos desagradables).

Sin embargo, FWIW, gcc solo da una advertencia para lo siguiente:

//
// 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
$ 

Qué compilador estas usando ?

  • Aquí hay un caso en el que le gustaría liberar un puntero a const: char const* s = strdup("hello"); free(s);.

    – bobbogo

    19 de julio de 2012 a las 9:44


  • @bobbogo: sí, aunque es difícil imaginar por qué querrías hacer una copia constante de un literal de cadena en primer lugar.

    – Pablo R.

    19 de julio de 2012 a las 10:46


  • Es posible que desee tomar una copia de una cadena que está a punto de ser liberada () o modificada por el código de la biblioteca. No vas a modificar tu copia, así que la marcas const.

    – bobbogo

    27 de agosto de 2012 a las 11:44

avatar de usuario
nlstd

Hay casos en los que desea liberar un const*. Sin embargo, no desea hacerlo a menos que lo asigne/asigne en la misma función. De lo contrario, es probable que rompas cosas. Consulte el código a continuación para ver un ejemplo del mundo real. yo suelo const en las declaraciones de funciones para mostrar que no estoy cambiando el contenido de los argumentos. sin embargo lo és reasignado con un duplicado en minúsculas (strdup) que debe liberarse.

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

No tiene sentido lanzar un puntero malloc’d a const. Cualquier función que tome un puntero const no debería ser responsable de liberar la memoria que se le pasó.

  • ¿Qué pasa con el código como struct foo { const char *bar; ... }? Esto expresa el hecho de que la memoria señalada por foo->bar deben ser tratados como inmutables (mientras que otros miembros de struct foo puede ser variable). Esto es útil para asegurar la corrección de un programa. Todavía bar puede necesitar ser malloc’d cuando el objeto se inicializa por primera vez. Si desea liberar un objeto de este tipo, necesita una forma de liberar bartambién.

    – uncleremo

    27 de octubre de 2020 a las 9:12

  • @uncleremus Esta es una cuestión de desde qué perspectiva está operando. La memoria a la que apunta foo->bar debe ser tratada como inmutable por el destinatario. La memoria a la que apunta foo->bar no debe ser tratada como inmutable por quien la posee, porque necesita desasignar esa memoria, lo que definitivamente constituye una mutación. Por lo tanto, debe mostrar a otras personas una interfaz inmutable mientras conserva una versión mutable para usted.

    – Cachorro

    28 de octubre de 2020 a las 10:27

  • ¿Estás sugiriendo usar una unión? struct foo { union { const char *bar; char *__bar; }; } funcionaría, supongo.

    – uncleremo

    29 de octubre de 2020 a las 13:19

  • @uncleremus, sugiero que debe tener dos estructuras completamente diferentes, una para uso interno y otra para uso externo.

    – Cachorro

    30 oct 2020 a las 14:33

  • Incluso el código de “propietario” puede necesitar modificar el bar elemento en un solo lugar (el destructor), mientras que podría pasar foo objetos mucho y posiblemente modificar otros miembros. Incluso en el código propietario de la estructura, protegiendo bar contra la modificación errónea con const es deseable. los const solo necesita ser desechado en el destructor.

    – uncleremo

    15 de enero de 2021 a las 13:34

avatar de usuario
tío

Varias respuestas han sugerido simplemente lanzar a char*. Pero como el.pescado escribió arriba,

fundición const a no-const es un síntoma del olor del código.

Hay advertencias del compilador que protegen contra esto, como -Wcast-qual en gcc, que me parece muy útil. Si usted De Verdad tener un caso válido para liberar a un const puntero (al contrario de lo que muchos han escrito aquí, hay están casos válidos, como lo señala nlstd), podría definir una macro para ese propósito de esta manera:

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

Esto funciona al menos para gcc. El doble reparto hace la lógica -Wcast-qual no detecte esto como “deshacerse de const”. No hace falta decir que esta macro debe usarse con cuidado. En realidad, solo debe usarse para punteros asignados en la misma función.

  • ¿Qué pasa con el código como struct foo { const char *bar; ... }? Esto expresa el hecho de que la memoria señalada por foo->bar deben ser tratados como inmutables (mientras que otros miembros de struct foo puede ser variable). Esto es útil para asegurar la corrección de un programa. Todavía bar puede necesitar ser malloc’d cuando el objeto se inicializa por primera vez. Si desea liberar un objeto de este tipo, necesita una forma de liberar bartambién.

    – uncleremo

    27 de octubre de 2020 a las 9:12

  • @uncleremus Esta es una cuestión de desde qué perspectiva está operando. La memoria a la que apunta foo->bar debe ser tratada como inmutable por el destinatario. La memoria a la que apunta foo->bar no debe ser tratada como inmutable por quien la posee, porque necesita desasignar esa memoria, lo que definitivamente constituye una mutación. Por lo tanto, debe mostrar a otras personas una interfaz inmutable mientras conserva una versión mutable para usted.

    – Cachorro

    28 de octubre de 2020 a las 10:27

  • ¿Estás sugiriendo usar una unión? struct foo { union { const char *bar; char *__bar; }; } funcionaría, supongo.

    – uncleremo

    29 de octubre de 2020 a las 13:19

  • @uncleremus, sugiero que debe tener dos estructuras completamente diferentes, una para uso interno y otra para uso externo.

    – Cachorro

    30 oct 2020 a las 14:33

  • Incluso el código de “propietario” puede necesitar modificar el bar elemento en un solo lugar (el destructor), mientras que podría pasar foo objetos mucho y posiblemente modificar otros miembros. Incluso en el código propietario de la estructura, protegiendo bar contra la modificación errónea con const es deseable. los const solo necesita ser desechado en el destructor.

    – uncleremo

    15 de enero de 2021 a las 13:34

avatar de usuario
Félix Kling

Puedo estar equivocado, pero creo que el problema radica en const. Echa el puntero a non-const como:

free((char *) p);

porque con const tu dices: No cambie los datos a los que apunta este puntero.

  • free no cambia el puntero. Libera el bloque de memoria al que apunta el puntero. Se trata de un error en la especificación del idioma. free claramente debe tomar un puntero const.

    – Axel Gneiting

    12 mayo 2010 a las 14:45


  • @Axel const significa que no puede cambiar el contenido del objeto de almacenamiento, no el valor real del puntero … ¡y liberar la memoria apuntada es un cambio bastante dramático, diría yo! (Por cierto, parece un poco pretencioso pensar que la especificación es incorrecta [and has been wrong for more than 30 years] y de repente descubres que tienes razón y todos los miembros de la junta de revisión no la tenían, ¿no es así?)

    – Fortran

    12 de mayo de 2010 a las 14:56


  • @fortran: no es nada pretencioso, es una diferencia de opinión común. delete en C++ se puede utilizar en un const char*, por lo que si se trata de una gran controversia, entonces uno u otro grupo de autores estándar debe estar equivocado. En realidad, no creo que realmente importe: descartar const para liberar un puntero no es una crisis.

    –Steve Jessop

    12 mayo 2010 a las 15:31


  • const char* dice que lo que se apunta es una constante y no se puede cambiar. Está no diciendo que el puntero en sí no se puede cambiar.

    – JeremyP

    12 mayo 2010 a las 15:55

  • @Axel Gneiting: Nunca dije que se cambia el puntero. const indica que el datos en esta ubicación no debe cambiarse. Pero si libera la memoria, los datos en esta ubicación se pueden sobrescribir y, por lo tanto, cambiar.

    – Félix Kling

    12 mayo 2010 a las 18:41


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad