Tratando de usar free() para entender cómo funciona


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 estoy recibiendo 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;
}
Author: Peter Mortensen, 2014-04-14

7 answers

Intentar liberar un puntero que no obtuviste de malloc (o de uno de sus amigos) causa un comportamiento indefinido. Su segunda llamada free(ptr) intenta eso.

De la especificación C, §7.22.3.3 La función free , párrafo 2:

El free la función hace que el espacio apuntado por ptr no se asignarán, es decir, estarán disponibles para nuevas asignaciones. Si ptr 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 a free o realloc, el comportamiento es indefinido.

 38
Author: Carl Norum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-14 05:28:04

Si bien todas las respuestas de comportamiento indefinido son correctas, ver una implementación podría ser más útil.

Creo que una muy buena referencia a esto es K&R C malloc. Una explicación de cómo funciona está en "El Lenguaje de programación en C", Capítulo 8.7.

Recuerde al mirar el código de las funciones de biblioteca estándar en C que tienen una implementación y una especificación, donde la implementación podría tener un comportamiento reproducible que no es requerido por su especificación.

Esencialmente casi todas las implementaciones de malloc tienen una lista libre donde administran regiones de memoria libres en una lista (o varias listas). La lista libre 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 se crean a propósito. Phrack tiene un artículo fechado sobre cómo invocar la ejecución de código al pasar memoria no válida a free mientras corrompe al freelist.

Otras implementaciones malloc también podrían valer la pena mirar (esta es una lista incompleta de bibliotecas de asignación).

 11
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-14 16:41:06
   ptr = &ret;
   free(ptr);

Es lo mismo que:

    free(&ret);

ret reside en la pila, no en el montón. Intentando free() memoria que no fue asignada (con malloc(), etc.) en el montón causará un comportamiento indefinido.

 11
Author: Mahonri Moriancumer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-15 09:19:07

La función free() solo funciona para la memoria que ha sido asignada en heap por malloc(). No para la asignación estática, porque 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 se debe liberar la memoria del montón.

 9
Author: Ashraful Haque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-15 05:15:43

Su segundo free(ptr) está causando un comportamiento indefinido al intentar liberar un puntero que no asignó. También tenga en cuenta que las asignaciones estáticas se reclaman automáticamente, por lo que no necesita que las liberen.

 8
Author: Rahul Tripathi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-14 16:37:48
  ptr = &ret;
   free(ptr);

Aquí está tratando de liberar la memoria de la variable de pila de almacenamiento local. Según la regla, no debes liberarlo. Cuando el main () sale toda la memoria de almacenamiento local en la pila siempre se libera.

Free solo funciona con la memoria de asignación de montones.

 5
Author: Jeegar Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-14 05:23:15

Hay tres áreas donde se pueden crear variables en un programa c o c++.

  • las variables globales o estáticas se encuentran en ubicaciones fijas en el archivo binario ejecutable.
  • las variables automáticas fuera del ámbito estático están en la pila
  • malloc ed o calloc ed variables están en el montón.

Free() es la función que libera la 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 manera para 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 mostrado, 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 la pila-bos) y crece aumentando, y un heap comienza en la dirección máxima (...FFFF, también conocido como -1) y crece por 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 de montón, por ejemplo, 16 bits addr: 0xFFFF, 32 bits addr: 0xFFFFFFFF o 64 bits addr: 0xffffffffffffff)

-2. (1ra ubicación abajo del principio del montón. 0x...FFFE) rpp [9], uno tiempo

-3. (2da ubicación hacia abajo desde el comienzo del montón. 0x...FFFD)

-4. (3ra ubicación abajo del principio del montón. 0x...FFFC) rpp[8 ], a la vez

[snip]

-17. (ubicación 16 desde el comienzo del montón. 0x...FFEF)

-18. (ubicación 17 desde el comienzo del montón. 0x...FFEE) rpp [1], al mismo tiempo

-19. (ubicación 18 desde el comienzo del montón. 0x...FFED)

-20 (ubicación 19 desde el comienzo del montón. 0x...FFEC) rpp [0 ], al mismo tiempo

-21. (parte superior del montón, 10 X 16 bits ints hacia abajo desde el principio del montón. 0x...FFEB), al mismo tiempo

: Una amplia gama 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 pila: sobre los datos estáticos ) Inicio de int devuelto desde Mail ()

Togs: (parte superior de global/estático) Final de "ptr"

: (el tamaño de un puntero es el ancho de bus de direcciones... lo que sea necesario)

Togs - (n-1): (top of global/static - (sizeof (int* ) - 1)) inicio 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 no del programa)

0 (inicio de código no-programa, 0x...0000 )


En tiempo de ejecución, "ptr" y "ret" son probablemente tanto inicio en '0', ya que son valores fijos, estáticos, leídos del archivo del que proviene el binario ejecutable.

A medida que se ejecuta el programa, el valor de "ptr" cambia, primero, para apuntar al montón, en la matriz malloc'ed de 10 ints: "0x...FFEC "

La llamada a free() no cambia el valor de ptr, sigue siendo "0x...FFEC" Free ' ing " 0x...FFEC " es de fiar y funciona sin nada divertido.

La asignación "ptr = & ret" establece un nuevo valor en "ptr", "(togs-n) -1" , el inicio de "ret".

Free ' ing "(togs-n)-1" causa un bloqueo inmediato porque "free" comprueba 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 como es global/estático, permanece en lo que era cuando el enlazador lo escribió en el disco.

 2
Author: Bill IV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-15 00:43:41