Ayuda del progtwig C: asignación de memoria insuficiente pero aún funciona … ¿por qué?

Posible duplicado:
comportamiento de malloc (0)

Estoy tratando de entender la asignación de memoria en C. Entonces estoy experimentando con malloc . Asigné 0 bytes para este puntero, pero aún puede contener un entero. De hecho, no importa qué número ponga en el parámetro de malloc , todavía puede contener cualquier número que le dé. ¿Por qué es esto?

 #include  #include  int main(void) { int *ptr = (int*)malloc(0); *ptr = 9; printf("%i", *ptr); // 9 free(ptr); return 0; } 

Todavía imprime 9, ¿qué pasa con eso?

Si el tamaño es 0, malloc () devuelve NULL o un valor de puntero único que luego se puede pasar con éxito a free ().

Supongo que estás golpeando el segundo caso. De todos modos, ese puntero, simplemente por error, se encuentra en un área donde puede escribir sin generar una falla de segmentación, pero probablemente esté escribiendo en el espacio de alguna otra variable desordenando su valor.

Muchas buenas respuestas aquí. Pero definitivamente es un comportamiento indefinido. Algunas personas declaran que un comportamiento indefinido significa que los dragones púrpuras pueden salir volando de tu computadora o algo así … probablemente haya algo de historia detrás de la indignante afirmación de que me estoy perdiendo, pero te prometo que los dragones púrpuras no aparecerán independientemente de Cuál será el comportamiento indefinido.

En primer lugar, permítanme mencionar que, en ausencia de una MMU, en un sistema sin memoria virtual, su progtwig tendría acceso directo a toda la memoria del sistema, independientemente de su dirección. En un sistema como ese, malloc() es simplemente el tipo que te ayuda a crear piezas de memoria de una manera ordenada; el sistema no puede obligarte a usar solo las direcciones que malloc() te dio. En un sistema con memoria virtual, la situación es ligeramente diferente … bueno, está bien, es muy diferente. Pero dentro de su progtwig, cualquier código en su progtwig puede acceder a cualquier parte del espacio de direcciones virtuales que se asigna a través de la MMU a la memoria física real. No importa si obtuvo una dirección de malloc () o si llamó a rand () y resultó que obtuvo una dirección que se encuentra en una región asignada de su progtwig; si está mapeado y no está marcado solo para ejecutar, puede leerlo. Y si no está marcado como de solo lectura, también puede escribirlo. Sí. Incluso si no lo malloc() de malloc() .

Consideremos las posibilidades para el comportamiento indefinido de malloc(0) :

  • malloc(0) devuelve NULL.

OK, esto es bastante simple. Realmente hay una dirección física 0x00000000 en la mayoría de las computadoras, e incluso una dirección virtual 0x00000000 en todos los procesos, pero el sistema operativo intencionalmente no asigna ninguna memoria a esa dirección para que pueda interceptar accesos de puntero nulo. Hay una página completa (generalmente 4KB) que nunca se mapea en absoluto, y tal vez incluso mucho más que 4KB. Por lo tanto, si intenta leer o escribir a través de un puntero nulo, incluso con un desplazamiento de él, llegará a estas páginas de memoria virtual que ni siquiera están asignadas, y la MMU lanzará una excepción (una excepción de hardware o una interrupción). ) que el sistema operativo detecta y declara un SIGSEGV (en Linux / Unix), o un acceso ilegal (en Windows).

  • malloc(0) devuelve una dirección válida a la memoria no asignada previamente de la unidad asignable más pequeña.

Con esto, realmente obtienes un pedazo de memoria real que legalmente puedes llamar tuyo, de un tamaño que no sabes. Realmente no deberías escribir nada allí (y probablemente tampoco leer) porque no sabes qué tan grande es, y para el caso, no sabes si este es el caso particular que estás experimentando (consulta lo siguiente) casos). Si este es el caso, es casi seguro que el bloque de memoria que recibió es de al menos 4 bytes y probablemente sea de 8 bytes o incluso más grande; todo depende de cualquiera que sea el tamaño de la unidad asignable mínima de su implementación.

  • malloc(0) devuelve intencionalmente la dirección de una página no asignada de la memoria que no sea NULL.

Esta es probablemente una buena opción para una implementación, ya que le permitiría a usted o al sistema rastrear y emparejar llamadas malloc () con sus llamadas free () correspondientes, pero en esencia, es lo mismo que devolver NULL. Si intenta acceder (lectura / escritura) a través de este puntero, bloqueará (SEGV o acceso ilegal).

  • malloc(0) devuelve una dirección en alguna otra página de la memoria asignada que puede ser utilizada por “alguien más”.

Me parece altamente improbable que un sistema disponible comercialmente tome esta ruta, ya que sirve para simplemente ocultar errores en lugar de eliminarlos lo antes posible. Pero si lo hiciera, malloc () devolvería un puntero a algún lugar en la memoria que usted no posee . Si este es el caso, seguro, puedes escribir en él todo lo que quieras, pero estarías corrompiendo la memoria de otro código, aunque sería memoria en el proceso de tu progtwig, así que puedes estar seguro de que al menos no estás Va a estar pisando la memoria de otro progtwig. (Escucho a alguien que se prepara para decir: “Pero es UB, así que técnicamente podría estar pisando fuerte la memoria de algún otro progtwig. Sí, en algunos entornos, como un sistema integrado, eso es correcto. Ningún sistema operativo comercial moderno permitiría que un proceso tuviera Sin embargo, acceda a la memoria de otro proceso tan fácilmente como simplemente llamando a malloc (0); de hecho, simplemente no puede pasar de un proceso a la memoria de otro proceso sin pasar por el sistema operativo para hacerlo por usted.) De todos modos, vuelva a la realidad. .. Este es el punto donde realmente comienza el “comportamiento indefinido”: si escribe en la “memoria de otra persona” (en el proceso de su propio progtwig), cambiará el comportamiento de su progtwig en un modo difícil de predecir Saber la estructura de su progtwig y dónde se presenta todo en la memoria, es totalmente predecible. Pero de un sistema a otro, las cosas se establecerían en la memoria (que aparecen en diferentes ubicaciones en la memoria), por lo que el efecto en un sistema no sería necesariamente el mismo que el efecto en otro sistema, o en el mismo sistema en un momento diferente.

  • Y finalmente … No, eso es. Realmente, en verdad, solo existen esas cuatro posibilidades. Podría argumentar a favor de puntos de subconjunto de casos especiales para los dos últimos de los anteriores, pero el resultado final será el mismo.

Por un lado, su comstackdor puede ver estas dos líneas consecutivas y optimizarlas:

 *ptr = 9; printf("%i", *ptr); 

Con un progtwig tan simple, su comstackdor puede estar realmente optimizando todo el ciclo de asignación / libre de memoria y usando una constante en su lugar. Una versión optimizada por comstackdor de su progtwig podría terminar pareciendo más simplemente:

 printf("9"); 

La única forma de saber si esto es realmente lo que está sucediendo es examinar el conjunto que emite su comstackdor. Si está intentando aprender cómo funciona C, recomiendo deshabilitar explícitamente todas las optimizaciones del comstackdor cuando construya su código.

Con respecto a su uso de malloc particular, recuerde que obtendrá un puntero NULL de vuelta si falla la asignación. Siempre verifique el valor de retorno de malloc antes de usarlo para cualquier cosa. La falta de referencia a ciegas es una buena manera de bloquear su progtwig.

El enlace que Nick publicó da una buena explicación sobre por qué malloc(0) parece funcionar (tenga en cuenta la diferencia significativa entre “works” y “parece funcionar”). Para resumir la información allí, malloc(0) puede devolver NULL o un puntero. Si devuelve un puntero, se le prohíbe expresamente usarlo para otra cosa que no sea pasarlo a free() . Si intenta usar dicho puntero, está invocando un comportamiento indefinido y no hay forma de saber qué sucederá como resultado. Puede parecer que funciona para usted, pero al hacerlo puede estar sobrescribiendo la memoria que pertenece a otro progtwig y dañando su espacio de memoria. En resumen: no puede pasar nada bueno, así que deja ese puntero solo y no pierdas el tiempo con malloc(0) .

La respuesta a las llamadas malloc(0) / free() no se cuelgan se puede encontrar aquí:

tamaño cero malloc

Acerca de *ptr = 9 , es como desbordar un búfer (como malloc’ing 10 bytes y acceder al 11), usted está escribiendo en una memoria que no posee, y al hacerlo está buscando problemas. En esta implementación particular, malloc(0) devuelve un puntero en lugar de NULL.

En pocas palabras, está mal incluso si parece funcionar en un caso simple.

Algunos asignadores de memoria tienen la noción de “tamaño mínimo asignable”. Entonces, incluso si pasa cero, esto devolverá el puntero a la memoria de tamaño de palabra, por ejemplo. Debe consultar la documentación del asignador de su sistema. Pero si devuelve el puntero a alguna memoria, sería incorrecto confiar en él, ya que se supone que el puntero solo se pasa para que se pase realloc () o free ().