test para online niños montreal lista imprimir habilidades gratis evaluación evaluacion cognitivo cognitivas cognitiva c gcc memory-management clang free

para - ¿Es cero() cero la memoria?



test de evaluacion cognitiva para niños (7)

¿Es cero () cero la memoria?

No. La implementación de glibc malloc puede sobrescribir hasta cuatro veces el tamaño de un puntero de los datos del usuario anterior para los datos internos de la limpieza.

Los detalles:

La siguiente es la estructura malloc_chunk de glibc (ver here ):

struct malloc_chunk { INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; /* Only used for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ struct malloc_chunk* bk_nextsize; };

La región de memoria para datos de usuario en un fragmento de memoria asignado comienza después de la entrada de size . Una vez free se denomina espacio de memoria donde se han utilizado los datos de usuario para listas de fragmentos de memoria libres, por lo que los primeros 4 * sizeof(struct malloc_chunk *) bytes de los datos de usuario anteriores probablemente se sobrescriban, de ahí otro valor que el anterior el valor de datos del usuario se imprime. Es un comportamiento indefinido. Si el bloque asignado es más grande, podría haber una falla de segmentación.

Hasta hoy vivía en la creencia de que llamar a free() en el espacio de la memoria lo libera para su posterior asignación sin ninguna otra modificación. Especialmente, considerando esta pregunta SO que establece claramente que free() NO pone a cero la memoria.

Sin embargo, consideremos este fragmento de código (test.c):

#include<stdlib.h> #include<stdio.h> int main() { int* pointer; if (NULL == (pointer = malloc(sizeof(*pointer)))) return EXIT_FAILURE; *pointer = 1337; printf("Before free(): %p, %d/n", pointer, *pointer); free(pointer); printf("After free(): %p, %d/n", pointer, *pointer); return EXIT_SUCCESS; }

Compilación (tanto GCC como Clang):

gcc test.c -o test_gcc clang test.c -o test_clang

Resultado:

$ ./test_gcc Before free(): 0x719010, 1337 After free(): 0x719010, 0 $ ./test_clang Before free: 0x19d2010, 1337 After free: 0x19d2010, 0

¿Por que es esto entonces? ¿Estaba viviendo en una mentira todo este tiempo o no entendí algunos conceptos básicos? ¿O hay una mejor explicación?

Alguna información técnica:

Linux 4.0.1-1-ARCH x86_64 gcc version 4.9.2 20150304 (prerelease) (GCC) clang version 3.6.0 (tags/RELEASE_360/final)


hay una mejor explicación?

Ahi esta. Si se desmarca un puntero después de haber sido free() d, se obtiene un comportamiento indefinido, por lo que la implementación tiene permiso para hacer lo que le plazca, incluido el hecho de engañarle para hacerle creer que la región de memoria se ha llenado de ceros.


Como otros señalaron, no se permite hacer nada con un puntero d (de lo contrario, es el temido comportamiento indefinido , que siempre se debe evitar, vea this ).

En la práctica, recomiendo nunca codificar simplemente

free(ptr);

pero siempre codificando

free(ptr), ptr=NULL;

(ya que, en términos prácticos, esto ayuda mucho a detectar algunos errores, excepto el doble de s free )

Si ptr no se usa después de eso, el compilador optimizaría omitiendo la asignación de NULL

En la práctica, el compilador sabe acerca de free y malloc (porque los encabezados de bibliotecas estándar C probablemente declararían estas funciones estándar con los atributos de función apropiados -entendidos por ambos GCC & Clang/LLVM ) para poder optimizar el código (según el estándar especificación de malloc & free ....), pero la implementación de malloc y free menudo es proporcionada por su biblioteca estándar C (por ejemplo, muy a menudo GNU glibc o musl-libc en Linux) por lo que el comportamiento real es proporcionado por su libc (no el compilador mismo). Lea la documentación apropiada, especialmente la página de manual free(3) .

Por cierto, en Linux, tanto glibc como musl-libc son software libre, por lo que puedes estudiar su código fuente para comprender su comportamiento. A veces obtenían memoria virtual del kernel usando una llamada al sistema como mmap(2) (y luego munmap(2) la memoria al kernel usando munmap(2) ), pero generalmente intentaban reutilizar memoria d previamente free para futuros malloc s

En la práctica, free podría munmap tu memoria (notablemente para las zonas malloc memory de memoria grande ) y luego obtendrás un SIGSEGV si te atreves a desreferenciar (más tarde) ese puntero d free , pero a menudo (especialmente para zonas de memoria pequeña ) lo haría simplemente logra reutilizar esa zona más tarde. El comportamiento exacto es específico de la implementación. Por lo general, free no borra ni escribe la zona recién liberada.

Incluso se le permite redefinir (es decir, volver a implementar) su propio malloc y free , tal vez mediante el enlace de una biblioteca especial como libtcmalloc , siempre que su implementación tenga un comportamiento compatible con lo que dicen los estándares C99 o C11.

En Linux, deshabilite el exceso de memoria y use Valgrind . Compile con gcc -Wall -Wextra (y probablemente -g al depurar, también podría considerar pasar -fsanitize=address a gcc reciente o clang al menos para buscar algunos errores -fsanitize=address ).

Por cierto, a veces el recolector de basura conservador de Boehm podría ser útil; GC_MALLOC (en todo tu programa) GC_MALLOC lugar de malloc y no te importará free memoria.


Hay otra trampa que quizás no hubieras sabido, aquí:

free(pointer); printf("After free(): %p /n", pointer);

Incluso solo reading el valor del pointer después de free es un comportamiento indefinido, porque el puntero se vuelve indeterminado.

Por supuesto, desreferenciar el puntero liberado, como en el ejemplo siguiente, tampoco está permitido:

free(pointer); printf("After free(): %p, %d/n", pointer, *pointer);

PD. En general, cuando se imprime una dirección con %p (como en printf ), printf a (void*) , p. Ej. (void*)pointer ; de lo contrario, obtendrá un comportamiento indefinido también


No hay una única respuesta definitiva a su pregunta.

  • En primer lugar, el comportamiento externo de un bloque liberado dependerá de si fue liberado al sistema o si se almacenó como un bloque libre en el grupo de memoria interna del proceso o biblioteca de tiempo de ejecución de C. En los sistemas operativos modernos, la memoria "devuelta al sistema" será inaccesible para su programa, lo que significa que la cuestión de si fue cero o no es irrelevante.

(El resto se aplica a los bloques retenidos en el grupo de memoria interna).

  • En segundo lugar, tiene poco sentido llenar la memoria liberada con cualquier valor específico (ya que no se supone que debe acceder a ella), mientras que el costo de rendimiento de dicha operación puede ser considerable. Por eso, la mayoría de las implementaciones no hacen nada para liberar memoria.

  • En tercer lugar, en la etapa de depuración la memoria liberada con algún valor de basura predeterminado puede ser útil para detectar errores (como el acceso a la memoria ya liberada), por lo que muchas implementaciones de depuración de la biblioteca estándar llenarán la memoria liberada con algún valor predeterminado o patrón. (Cero, por cierto, no es la mejor opción para ese valor. Algo como el patrón 0xDEADBABE tiene mucho más sentido). Pero nuevamente, esto solo se hace en versiones de depuración de la biblioteca, donde el impacto en el rendimiento no es un problema.

  • En cuarto lugar, muchas implementaciones (la mayoría) populares de la administración de la memoria de pila usarán una porción del bloque liberado para sus fines internos, es decir, almacenan allí algunos valores significativos. Lo que significa que esa área del bloque se modifica por free . Pero en general no es "cero".

Y todo esto es, por supuesto, altamente dependiente de la implementación.

En general, su creencia original es perfectamente correcta: en la versión de lanzamiento del código, un bloque de memoria liberado no está sujeto a modificaciones de todo el bloque.


free() no pone a cero la memoria como regla general. Simplemente lo libera para ser reutilizado por una llamada futura a malloc() . Ciertas implementaciones pueden llenar la memoria con valores conocidos, pero eso es puramente un detalle de implementación de la biblioteca.

El tiempo de ejecución de Microsoft hace un buen uso del marcado de la memoria liberada y asignada con valores útiles (consulte En Visual Studio C ++, ¿cuáles son las representaciones de asignación de memoria? Para obtener más información). También lo he visto lleno de valores que, al ejecutarse, causarían una trampa bien definida.


free() puede devolver memoria al sistema operativo y reducir el proceso. Normalmente, todo lo que puede hacer es permitir una llamada posterior a malloc para reutilizar el espacio . Mientras tanto, el espacio permanece en su programa como parte de una lista gratuita utilizada internamente por malloc .