Uso de realloc para reducir la memoria asignada

7 minutos de lectura

avatar de usuario
Moshé Rosenschein

Pregunta simple sobre la función realloc en C: si uso realloc para reducir el bloque de memoria al que apunta un puntero, ¿se libera la memoria “extra”? ¿O es necesario liberarlo manualmente de alguna manera?

Por ejemplo, si hago

int *myPointer = malloc(100*sizeof(int));
myPointer = realloc(myPointer,50*sizeof(int));
free(myPointer);

¿Tendré una pérdida de memoria?

  • Estrictamente hablando, hay una pérdida de memoria, ya que no registra el resultado de realloc y por lo tanto no es posible liberarlo. Pero como indica la respuesta de R.., es posible que haya tenido suerte con un detalle de implementación.

    –Steve Jessop

    16 de agosto de 2011 a las 12:34


  • Uy tienes razón. He intentado corregirlo. ¿Que tal ahora?

    – Moshé Rosenschein

    16 de agosto de 2011 a las 12:39

  • El nuevo código aún filtra la asignación original si el realloc falla Espero que la mayoría de las implementaciones nunca dejen de reducir un bloque, pero está permitido. La forma correcta de llamar a realloc, ya sea aumentando o reduciendo el bloque, es void *tmp = realloc(myPointer, 50*sizeof(int)); if (!tmp) { /* handle error somehow. myPointer still points to the old block, which is still allocated */ } myPointer = tmp;.

    –Steve Jessop

    16 de agosto de 2011 a las 12:46


avatar de usuario
cnicutar

No, no tendrás una pérdida de memoria. realloc simplemente marcará el resto como “disponible” para el futuro malloc operaciones.

Pero todavía tienes que free myPointer mas tarde. Aparte, si usas 0 como el tamaño en realloctendrá el mismo efecto que free en algunas implementaciones. Como dijeron Steve Jessop y R.. en los comentarios, no debes confiar en él.

  • “si usa 0 como tamaño en realloc, tendrá el mismo efecto que gratis”. – podría ser cierto en su implementación, pero no está garantizado. Si realloc devuelve un puntero nulo de una entrada 0 y no estableció errno en ENOMEM, luego se liberó la memoria. Pero al igual que malloc, realloc puede intentar devolver una asignación real de 0 tamaño utilizable. Se requiere la implementación para documentar cuál (7.20.3/1).

    –Steve Jessop

    16 de agosto de 2011 a las 12:27


  • Steve tiene razón, y la consecuencia práctica de esto es que nunca deberías llamar realloc con un tamaño de 0. Tratar con todos los comportamientos posibles, la existencia de implementaciones no conformes y el hecho de que la gente de C y POSIX parece estar en desacuerdo sobre lo que es conforme y lo que no hace que sea una muy mala idea confiar en cualquier cosa relacionada para realloc(x,0).

    – R.. GitHub DEJA DE AYUDAR A ICE

    16 de agosto de 2011 a las 12:33

  • @Steve Jessop, R.. Gracias por aclarar 🙂 Nunca me molesté en verificar más allá de POSIX, no sabía que no es un estándar C.

    – cnicutar

    16 de agosto de 2011 a las 12:39

  • @cnicutar: Posix también permite asignaciones reales de tamaño 0. No sé a qué desacuerdo se refiere R.., pero debe ser una diferencia en la interpretación del estándar C, porque no es tan simple como Posix simplemente restringiendo el comportamiento permitido.

    –Steve Jessop

    16 de agosto de 2011 a las 12:42


  • POSIX dice “Si el tamaño es 0 y ptr no es un puntero nulo, el objeto al que se apunta se libera”. ISO C no hace este requisito, y de hecho, si la implementación toma la opción de tener malloc(0) devuelve un puntero único en lugar de NULL, parece contradecir los requisitos de C. Todo es lo suficientemente confuso. No creo que las aplicaciones deban siquiera tratar de lidiar con eso, pero simplemente aléjate de las tonterías…

    – R.. GitHub DEJA DE AYUDAR A ICE

    16 de agosto de 2011 a las 14:55

Definitivamente no hay una pérdida de memoria, pero cualquiera de al menos 3 cosas podría suceder cuando llame realloc para reducir el tamaño:

  1. La implementación divide el bloque de memoria asignado en la nueva longitud solicitada y libera la parte no utilizada al final.
  2. La implementación realiza una nueva asignación con el nuevo tamaño, copia el contenido anterior en la nueva ubicación y libera toda la asignación anterior.
  3. La implementación no hace nada en absoluto.

La opción 3 sería una implementación bastante mala, pero perfectamente legal; todavía no hay “pérdida de memoria” porque todo se liberará si llama más tarde free en eso.

En cuanto a las opciones 1 y 2, cuál es mejor depende mucho de si favoreces el rendimiento o evitas la fragmentación de la memoria. Creo que la mayoría de las implementaciones del mundo real se inclinarán por hacer la opción 1.

  • ¿Cuál es el razonamiento detrás de la opción 2 cuando necesita reducir la memoria asignada? No puedo resolverlo.

    – jacob

    16 de agosto de 2011 a las 14:31

  • Suponga que asignó 100 bytes y desea reducir el tamaño a 50 bytes. La primera asignación reclamó una zona libre de 100 bytes y la opción 1 devuelve una zona libre de 50 bytes, pero la zona más larga de 100 bytes ya no está disponible. Sin embargo, si hubiera otra zona libre de solo 50 bytes, la opción 2 podría mover los datos a esa ubicación y liberar una zona de 100 bytes, dejando la memoria menos fragmentada.

    – R.. GitHub DEJA DE AYUDAR A ICE

    16 de agosto de 2011 a las 14:44

  • Tenga en cuenta que si su objetivo es optimizar para una fragmentación mínima, solo tiene sentido tomar la opción 2 cuando ya existe una zona libre más pequeña lo suficientemente grande como para contener la asignación. Si tiene que obtener más memoria del sistema o dividir otra zona libre más grande, solo puede empeorar la fragmentación, no mejorarla.

    – R.. GitHub DEJA DE AYUDAR A ICE

    16 de agosto de 2011 a las 14:47

  • Ah, ya veo ; por lo que minimizamos la fragmentación a expensas del tiempo necesario para asignar un nuevo bloque y copiar datos. Además, supongo que no hay forma de controlar ese comportamiento.

    – jacob

    16 ago 2011 a las 15:00

  • La mejor manera de controlar este comportamiento es asignar lo que necesita en lugar de sobreasignar y cambiar el tamaño. No estoy al tanto de si alguna implementación realmente toma la opción 2, pero probablemente sea una buena idea, especialmente porque solo afectaría asignaciones relativamente pequeñas donde el costo de copia es bajo. (En un sistema operativo moderno, el kernel administra directamente las grandes asignaciones y se pueden reubicar en nuevas direcciones con espacio contiguo a través de reasignaciones de memoria virtual).

    – R.. GitHub DEJA DE AYUDAR A ICE

    16 de agosto de 2011 a las 15:28

El nuevo código sigue filtrando la asignación original si falla la reasignación. Espero que la mayoría de las implementaciones nunca dejen de reducir un bloque, pero está permitido. La forma correcta de llamar a realloc, ya sea aumentando o reduciendo el bloque, es void *tmp = realloc(myPointer, 50*sizeof(int)); if (!tmp) { /* manejar el error de alguna manera. myPointer todavía apunta al bloque anterior, que todavía está asignado */ } myPointer = tmp;. – Steve Jessop hace 48 minutos

Oye, no pude averiguar cómo responder a tu comentario, lo siento.

¿Necesito convertir tmp al tipo de myPointer? En este caso, ¿necesito escribir

myPointer = (int*)tmp

Además, en este caso, cuando libero (myPointer), la memoria a la que apunta tmp también se liberará, ¿verdad? Así que no hay necesidad de hacer

free(myPointer)
free(tmp)

  • Realloc seguirá goteando. Esta es la respuesta correcta. Al menos en Windows 7, 64 bits (tdm-gcc64)

    – DNS

    30 de diciembre de 2013 a las 16:06


Por la forma en que ha dado su código, sí, podría tener una fuga. La idea de realloc es que puede devolverte una nueva ubicación de tus datos. Como lo haces en tu pregunta, pierdes ese puntero que realloc te envía

int *myPointer2 = realloc(myPointer,50*sizeof(int));
assert(myPointer2); 
myPointer = myPointer2;

Supongo que el proceso para realloc() es, primero desasigna el bloque de memoria anterior y luego lo vuelve a asignar (mi profesor universitario me dijo eso). Si ese sigue siendo el caso, ya habría liberado los 50 bytes adicionales. Y si hay datos en la memoria de 100 bytes, se asignarán a la memoria de 50 bytes reasignados, lo que puede provocar la pérdida de datos. Así que no debería haber una fuga de memoria.

¿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