2012-03-05 13 views
21

Vamos a considerar esta muy corto fragmento de código:¿Puedo asumir que llamar a realloc con un tamaño más pequeño liberará el resto?

#include <stdlib.h> 

int main() 
{ 
    char* a = malloc(20000); 
    char* b = realloc(a, 5); 

    free(b); 
    return 0; 
} 

Después de leer la página del manual de realloc, no estaba del todo seguro de que la segunda línea haría que los 19995 bytes adicionales a ser liberados. Para citar la página man: The realloc() function changes the size of the memory block pointed to by ptr to size bytes., pero a partir de esa definición, ¿puedo estar seguro de que se liberará el resto?

Quiero decir, el bloque apuntado por b ciertamente contiene 5 bytes libres, ¿entonces sería suficiente que un asignador de cumplimiento lento simplemente no haga nada por la línea de realojo?

Nota: El asignador utilizo parece liberar a los 19 995 bytes adicionales, como se muestra por valgrind al comentar la línea free(b):

==4457== HEAP SUMMARY: 
==4457==  in use at exit: 5 bytes in 1 blocks 
==4457== total heap usage: 2 allocs, 1 frees, 20,005 bytes allocated 
+0

El encabezado '' no está definido por el estándar: prefiere usar ''. También arrojar el valor de retorno de 'malloc' (o' realloc') no sirve para nada útil y puede ocultar un error (* la representación de 'void *' y 'int' es diferente *) que el compilador hubiera detectado de otra manera. – pmg

+1

["El tamaño del bloque de memoria apuntado por el parámetro ptr se cambia a los bytes de tamaño, expandiendo o reduciendo la cantidad de memoria disponible en el bloque."] (Http://www.cplusplus.com/reference/clibrary/cstdlib/realloc /) –

+0

@pmg ok No lo sabía. Cambiaré en mi fragmento – qdii

Respuesta

19

Sí, garantizado por el estándar C si se puede asignar el nuevo objeto.

(C99, 7.20.3.4p2) "La función realloc desasigna el objeto antiguo apuntado por ptr y devuelve un puntero a un nuevo objeto que tiene el tamaño especificado por tamaño."

+0

[El borrador C99] (http://people.freebsd.org/~green/c9x-draft.txt) nunca menciona cualquier cosa contra realloc no haciendo nada cuando el viejo tamaño es igual al nuevo. Supongo que todas las implementaciones lo violan? – qdii

+3

C99 7.20.3.4 §4: * La función realloc devuelve un puntero al objeto nuevo (que puede tener el mismo valor que un puntero al objeto antiguo) * – Christoph

15

Sí — si tiene éxito.

el fragmento de código muestra un conocido, error nefasto:

char* b = (char*) realloc(a, 5); 

Si esto tiene éxito, la memoria que se asignó previamente a a será liberado, y b apuntará a 5 bytes de memoria que puede o no puede superponerse al bloque original.

Sin embargo, si falla la llamada, b habrá null, pero a todavía apuntará a su memoria original, que seguirá siendo válida. En ese caso, necesita free(a) para liberar la memoria.

es aún peor si se utiliza el (peligroso) idioma común:

a = realloc(a, NEW_SIZE);  // Don't do this! 

Si la llamada a realloc falla, una habrá null y quedarán huérfanos de su memoria original, por lo que es irremediablemente perdido hasta su programa salidas.

+0

Tienes razón. Yo había visto a cppcheck quejarse de esto. Supongo que este es un asesino para las pruebas de examen, pero realmente, ¿alguna vez esto sucede? – qdii

+0

@qdii: en el mundo integrado todas las apuestas están apagadas; Además, el sistema operativo podría imponer algunos límites, por ejemplo, en el espacio de memoria virtual a través de 'ulimit' – Christoph

+0

Sí, esto realmente sucede en el mundo real. ¿Por qué crees que tantos errores de software fallan cuando te quedas sin memoria (en un sistema sin intercambio) en lugar de darte un mensaje de que no puede realizar la operación solicitada debido a la falta de memoria? –

2

Esto depende de su implementación de libc. Todo lo que sigue es el comportamiento conformes:

  • no hacer nada, es decir, dejar que se mantienen los datos de donde está y devolver el bloque antiguo, posiblemente re-uso de los bytes ahora no utilizados para nuevas asignaciones (que yo sepa esta reutilización es no es común)
  • copiar los datos a un nuevo bloque y la liberación de la anterior vuelta al sistema operativo
  • copiar los datos a un nuevo bloque y retener el anterior para nuevas asignaciones

también es posible a

  • devolver un puntero nulo si un nuevo bloque no se puede asignar

En este caso, los datos antiguos también permanecer donde está, que puede conducir a pérdidas de memoria, por ejemplo, si la devolución el valor de realloc() sobrescribe la única copia del puntero a ese bloque.

Una implementación de libc razonable usará cierta heurística para determinar qué solución es más eficiente.

También tenga en cuenta que esta descripción se encuentra en el nivel de implementación: semánticamente, realloc() siempre libera el objeto siempre que la asignación no falle.

+1

En cuanto a "afaik tal reutilización no es común", nunca he oído hablar de una implementación en el mundo real que deje la asignación anterior en su lugar y no libere la cola para su reutilización. Esta sería una conducta patológicamente mala, aunque legal. –

+0

¿Existen estándares que definan este comportamiento, o realmente depende de la implementación de libc? –

+1

@ rr-: el estándar C así como POSIX lo deja en manos de la implementación; No conozco ninguna especificación que defina el comportamiento, pero podría estar documentada en el manual de libc – Christoph

0

Parece poco probable que 19995 bytes se hayan liberado. Lo más probable es que realloc haya reemplazado el bloque de 20000 bytes por otro bloque de 5 bytes, , es decir, que se haya liberado el bloque de 20000 bytes y se haya asignado un nuevo bloque de 5 bytes.

1

La función realloc tiene el siguiente contrato:

void * resultado = realloc (PTR, nuevo_tam)

  • Si el resultado es NULL, entonces PTR sigue siendo válida y sin cambios.
  • Si el resultado no es NULL, entonces ptr ahora no es válido (como si se hubiera liberado) y no volverá a utilizarse. el resultado ahora es un puntero a bytes de datos de new_size.

Los detalles precisos de lo que ocurre bajo el capó son de aplicación específica - por ejemplo, resultado puede ser igual a ptr (pero el espacio adicional más allá de nuevo_tam no se debe tocar nunca más) y realloc puede llamar libre, o puede hacer su propia representación libre interna. La clave es que, como desarrollador, usted ya no es responsable de ptr si realloc devuelve no nulo, y usted todavía tiene la responsabilidad si realloc devuelve NULL.

+0

Además, pasar new_size = 0 es lo mismo que libre (ptr) y devuelve NULL. Ese sería un caso donde result == NULL y ptr ya no es válido. – gnasher729

Cuestiones relacionadas