reserva - Tratando de usar free() para entender cómo funciona
reserva de memoria c++ (7)
Para entender el uso de free en el lenguaje de programación C intenté ejecutar este código en Ubuntu, pero al ejecutar el archivo EXE recibo un error SIGABRT. ¿Por qué el programa no sale normalmente?
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret;
int *ptr;
ptr = (int *)malloc(sizeof(int)*10);
free(ptr);
ptr = &ret;
free(ptr);
return 0;
}
Hay tres áreas donde se pueden crear variables en el programa ac o c ++.
- las variables globales o estáticas están en ubicaciones fijas en el archivo binario ejecutable.
- las variables automáticas fuera del alcance estático están en la pila
- las variables malloc''ed o calloc''ed están en el montón.
free () es la función que libera memoria previamente asignada en el montón. Un puntero a la memoria en el montón es devuelto por malloc o funciones similares. La única forma de leer o escribir esa memoria es a través del puntero. Un puntero es una dirección, y un puntero * es el contenido apuntado a esa dirección.
En el ejemplo que se muestra, hay dos variables, definidas en Main, y efectivamente estáticas, y el valor devuelto desde Main, que está en la pila. Usando el miniumum int, 16 bits, aquí hay un posible mapa de memoria. En este mapa, las instrucciones comienzan en 0, la pila comienza en algún valor distinto de cero, (comienzo de stack - bos) y crece al incrementarse, y un montón comienza en la dirección máxima (... FFFF, aka -1) y crece decrememeting:
(Recuerde, MIN_INT es -32768, MAX_INT es 32767 ... la especificación garantiza solo 16 bits, firmados)
Cada byte tiene una dirección que es ''n'' bits de ancho: 16, 32 o 64 bits, típicamente
-1. (inicio del montón, por ejemplo, 16 bit addr: 0xFFFF, 32 bit addr: 0xFFFFFFFF o 64 bit addr: 0xFFFFFFFFFFFFFFFF)
-2. (1ra ubicación hacia abajo desde el comienzo del montón. 0x ... FFFE) ptr [9], al mismo tiempo
-3. (2da ubicación hacia abajo desde el comienzo del montón. 0x ... FFFD)
-4. (3ra ubicación hacia abajo desde el comienzo del montón. 0x ... FFFC) ptr [8], al mismo tiempo
[recorte]
-17. (16ª ubicación hacia abajo desde el comienzo del montón. 0x ... FFEF)
-18. (17ª ubicación hacia abajo desde el comienzo del montón. 0x ... FFEE) ptr [1], al mismo tiempo
-19. (Ubicación 18 hacia abajo desde el comienzo del montón. 0x ... FFED)
-20 (ubicación 19 hacia abajo desde el comienzo del montón. 0x ... FFEC) ptr [0], al mismo tiempo
-21. (arriba del montón, 10 X 16 bit hacia abajo desde el comienzo del montón. 0x ... FFEB), al mismo tiempo
: Una gran variedad de direcciones en máquinas de 32 o 64 bits ...:
tos: (Parte superior de la pila 0x ... tos)
bos + (sizeof (int) - 1) Fin de int devuelto desde Main ()
bos: (principio de la pila: datos estáticos anteriores) Inicio de int devuelto por Correo ()
togs: (parte superior de global / estática) Fin de "ptr"
: (el tamaño de un puntero es el ancho del bus de direcciones ... lo que sea necesario)
togs- (n-1): (arriba de global / estático - (sizeof (int *) - 1)) comienzo de "ptr"
(togs-n): Fin de "ret"
(togs-n) -1: inicio de "ret"
(cualquier cosa global que el compilador agregue para sí mismo, el depurador, etc.)
(fin del código del programa)
(inicio del código del programa)
(parte superior del código que no pertenece al programa)
0 (inicio del código que no es del programa, 0x ... 0000)
En tiempo de ejecución, "ptr" y "ret" probablemente comiencen en ''0'', ya que son fijos, estáticos, valores, leídos del archivo del que proviene el binario ejecutable.
A medida que el programa se ejecuta, el valor de "ptr" cambia, primero, para apuntar al montón, en la matriz malloc de 10 entradas: "0x ... FFEC"
La llamada a free () no cambia el valor de ptr, sigue siendo "0x ... FFEC" Free''ing "0x ... FFEC" es legítimo y funciona sin nada gracioso.
La asignación "ptr = & ret" establece un nuevo valor en "ptr", "(togs-n) -1", el inicio de "ret".
Free''ing "(togs-n) -1" provoca un bloqueo inmediato porque "libre" verifica el valor de "(togs-n) -1" y NO está en el rango válido para una dirección de montón.
"ret" permanece en blanco, nunca se establece, pero dado que es global / estático, permanece en lo que fue cuando el vinculador lo escribió en el disco.
Intentar liberar un puntero que no malloc
de malloc
(o uno de sus amigos) causa un comportamiento indefinido. Su segunda llamada free(ptr)
intenta precisamente eso.
De la especificación C, §7.22.3.3 La función free
, párrafo 2:
La función
free
hace que el espacio apuntado porptr
sea desasignado, es decir, que esté disponible para una asignación posterior. Siptr
es un puntero nulo, no se produce ninguna acción. De lo contrario, si el argumento no coincide con un puntero devuelto anteriormente por una función de gestión de memoria, o si el espacio ha sido desasignado por una llamada afree
orealloc
, el comportamiento no está definido.
La función free()
solo funciona para la memoria que ha sido asignada en heap
por malloc()
. No para la asignación estática, ya que las asignaciones estáticas se manejan automáticamente. Aquí está el error:
int ret;
ptr = &ret;
free(ptr);
No puede hacer esto porque la memoria para ret
no está asignada en el montón. Está en la pila y solo debe liberarse el recuerdo del montón.
Si bien todas las respuestas de comportamiento no definidas son correctas, ver una implementación podría ser más útil.
Creo que una muy buena referencia es K & R C malloc . Una explicación de cómo funciona es en "The C Programming Langugage" , Capítulo 8.7.
Recuerde que al mirar el código de las funciones de biblioteca estándar en C, tienen una implementación y una especificación, donde la implementación puede tener un comportamiento reproducible que no es requerido por su especificación.
Esencialmente, casi todas las implementaciones de malloc
tienen una lista libre en la que administran regiones de memoria libres en una lista (o múltiples listas). La lista gratuita a menudo se maneja de una manera, que llamando free
en una región de memoria varias veces pondrá esta lista en un estado incorrecto.
También se puede invocar el comportamiento malicioso cuando las estructuras de datos están diseñadas a propósito. Phrack tiene un artículo fechado sobre cómo invocar la ejecución del código al pasar memoria no válida a free
vez que corrompe la libreta.
Otras implementaciones malloc también pueden valer la pena mirar (esta es una lista incompleta de bibliotecas de asignación).
Su segundo free(ptr)
está causando un comportamiento indefinido ya que está intentando liberar un puntero que no asignó. También tenga en cuenta que las asignaciones estáticas se reclaman automáticamente, por lo que no es necesario que lo liberen.
ptr = &ret;
free(ptr);
es lo mismo que:
free(&ret);
ret
reside en la pila, no en el montón. Intentar free()
memoria que no fue asignada (con malloc()
, etc.) en el montón causará un comportamiento indefinido.
ptr = &ret;
free(ptr);
Aquí está tratando de liberar la memoria de la variable de la pila de almacenamiento local. Según la regla, no debes liberarlo. Cuando el principal () sale de la memoria de almacenamiento local en la pila, siempre se libera.
Solo funciona gratis con la memoria de asignación de Heap.