2009-12-21 9 views
21

A menudo me sorprendo a mí mismo haciendo lo siguiente (en los componentes no críticos):¿Siempre revisa la memoria malloc'ed?

some_small_struct *ptr=(some_small_struct *) malloc(sizeof(some_small_struct)); 
ptr->some_member= ...; 

En palabras, asignar dinámicamente la memoria para una estructura pequeña y lo uso directamente sin comprobar el puntero malloc'ed. Entiendo que siempre hay una posibilidad de que el programa no obtenga la memoria que pide (¡duh!), Pero considere lo siguiente:

Si el programa no puede obtener memoria para una estructura pequeña montón, tal vez hay problemas mucho más grandes que se avecinan y no importa después de todo.

Además, ¿qué pasa si el manejo del puntero nulo agrava aún más la situación precaria? (por ejemplo, intentar registrar la condición requiere aún más recursos no existentes, etc.)

¿Está en mi sano juicio (suficiente)?

Actualizado:

función
  1. A "safe_malloc" puede ser útil para depurar y podría ser útil lo contrario
  2. +X acceso puede ocultar la causa raíz de un puntero NULL
  3. En Linux, " Asignación de memoria optimista "puede sombrear las condiciones de OOM (sin memoria) del loomin
+0

Mejores prácticas significa fuerza y ​​estabilidad para todos los sistemas. Aplicarlos depende de nosotros. – lsalamon

+0

Encontré una pregunta relacionada (Me gusta la contribución de Reed Copsey): http://stackoverflow.com/questions/691402/do-you-still-trap-memory-allocation-failures-in-your-c-program – jldupont

Respuesta

18

Depende de la plataforma. Por ejemplo, en Linux (por defecto) que no tiene mucho sentido para comprobar NULL:

http://linux.die.net/man/3/malloc

Por defecto, Linux sigue una estrategia de asignación de memoria optimista. Esto significa que cuando malloc() devuelve un valor no nulo, no hay garantía de que la memoria esté realmente disponible. Este es un error realmente malo. En caso de que el sistema se quede sin memoria, el infame asesino OOM matará uno o más procesos.

+1

Al menos ... +1 !! – Ben

+6

Malloc aún puede devolver NULL cuando el espacio de direcciones está lleno. –

+0

+1: vuelve a mi punto. – jldupont

2

por lo menos lo pondría un assert(ptr != NULL) allí para que obtenga un error significativo.

+0

@Evan: pero, en mi opinión, ¿esto no desencadenaría una cadena de eventos que de todos modos no podría ser respaldada? – jldupont

+0

si la afirmación falla, el programa termina inmediatamente. ¿A qué cadena de eventos te refieres? –

+3

@jldupont: Sí, pero piense en esto: Imagine que ptr-> some_member = ... no causa una segfault, pero sobrescribe algo que causa un segfault mucho más tarde. Un error muy desagradable está bien escondido. El afirmar lo atrapará de inmediato. –

0

Es posible asignar una gran cantidad de memoria al inicio que puede liberar cuando se sale de una condición de falta de memoria y usarlo para apagar con gracia.

+0

@Eclipse: sí, por supuesto (el patrón de "memoria constante"), pero de eso no se trata la pregunta. – jldupont

+2

Esto realmente no funcionará en Linux, que felizmente se comprometerá en exceso con la memoria (ver también OOM killer). Su bloque "libre" será una sección reservada del espacio de direcciones sin memoria real que lo respalde, de modo que cuando lo libere, las cosas no mejoren. :: suspiro :: – dmckee

+0

Tendrá algún nivel de memoria de pila restante (de lo contrario, habría golpeado un desbordamiento de pila, no un retorno NULO de malloc). A menos que aciertes ambas condiciones al mismo tiempo, no deberías tener problemas para manejar la situación, siempre y cuando no necesites asignar más espacio en el montón hasta que puedas liberar la memoria de reserva. – Eclipse

2

Además, ¿qué pasa si el manejo del puntero nulo agrava aún más la situación precaria?

No veo por qué puede agravar la situación.
De todos modos, al escribir código para windows ptr-> some_member arrojará una infracción de acceso para que veas inmediatamente el problema, por lo tanto no veo ninguna razón para verificar el valor de retorno, a menos que tu programa tenga alguna oportunidad de liberar la memoria. Para plataformas que no manejan null-pointers en una buena forma (lanzando excepciones) es peligroso ignorar tales puntos.

+0

+1: exactamente. ¿Qué plataformas no manejarían null-pointers? (aparte de los sistemas integrados probablemente) – jldupont

+1

incluso si 'ptr' es un puntero nulo (es decir, su valor se encuentra en una región reservada del espacio de direcciones), eso no es necesariamente cierto para' ptr-> foo' o 'ptr [42]' – Christoph

+0

@Christoph: excelente punto. Tal vez el ajuste agregado estaría en el acceso a '+ 0' primero. – jldupont

7

Yo diría que no. El uso de un puntero NULL va a bloquear el programa (probablemente).
Pero detectarlo y hacer algo inteligente estará bien y es posible que pueda recuperarse de la situación de poca memoria.

Si está realizando una gran operación, configure un indicador de error global y comience a desenrollar la pila y liberar recursos. Con suerte, uno o más de estos recursos serán su memoria y su aplicación volverá a la normalidad.

Esto, por supuesto, es un problema de C y se maneja automáticamente en C++ con la ayuda de excepciones y RAII.
Como nuevo no devolverá NULL, no tiene sentido verificarlo.

+0

@Martin: el sistema (como también señaló Neil) probablemente dejará de responder al punto de que cualquier recuperación podría ser una operación inútil. Si algo está comiendo memoria más rápido de lo que se puede mitigar significativamente, no veo sentido. – jldupont

+0

@Martin: hmmm ... Todavía no estoy convencido. Veo el espectro de Heisenberg aquí ... – jldupont

+1

C++ logra hacerlo. Si algo está consumiendo memoria rápidamente, entonces deja de hacer algo. Libere todos los recursos por hacer con algo vuelva a un estado normal y luego informe el error. Con el trabajo C se puede hacer que maneje errores como C++. En el peor de los casos, llame a exit() y cierre de manera limpia. Generar segfaults porque puede molestarse en codificar correctamente no hará que sus clientes confíen en su capacidad. –

11

En el caso de C, depende de la plataforma. Si estás en una plataforma incrustada con muy poca memoria, deberías verificar, aunque lo que haces si falla es más difícil de decir. En un sistema operativo moderno de 32 bits con memoria virtual, el sistema probablemente dejará de responder y se bloqueará antes de admitir que se está quedando sin memoria. En este caso, la llamada a malloc nunca regresa, por lo que la utilidad de verificar su valor se vuelve irrelevante.

En el caso de C++, debe usar new en lugar de malloc, en cuyo caso se generará una excepción al agotarse, por lo que no tiene sentido comprobar el valor de retorno.

+4

Pero en el caso C, siempre debe verificar. Sin respuesta y recuperable es mejor que un bloqueo. –

+0

+1: mis pensamientos exactamente. Gracias. – jldupont

+0

@Martin: soy un discípulo de la doctrina del "fracaso rápido". – jldupont

1

Sí, tener memeoría insuficiente presagia casi de manera certero otros fallos que llegarán pronto. Pero ¿Cuán seguro está de que no se producirá ningún resultado corrupto entre la falla de asignación y el bloqueo final?

¿Qué tan seguro está usted de todos los programas , cada vez que realice una edición.

Coja sus errores para que pueda saber que se estrelló a tiempo.

+0

+1: para abordar la incertidumbre. Esta estrategia junto con 'safemalloc' podría ser una buena combinación. Gracias. – jldupont

0

Siempre siento que es importante y es mejor manejar la devolución de malloc o cualquier otra llamada al sistema para ese asunto. Aunque en los sistemas modernos (aparte de los integrados) es un escenario raro a menos que y hasta que su código use demasiada memoria, siempre es más seguro.

Continuar con el código después de una falla en la llamada al sistema puede provocar corrupción, bloqueo y lo que no sea hacer que su programa se vea mal.

Además, en Linux, la memoria asignada a un proceso es limitada. Intente crear 1000 hilos en un proceso y asigne un poco de memoria en cada uno de ellos, luego puede simular fácilmente la condición de poca memoria. :)

¡Siempre es mejor verificar los valores de devolución de llamadas del sistema!

2

Suponiendo que se ejecuta en un Linux/MaxOs/Windows u otro sistema de memoria virtual, entonces ... la única razón para verificar el valor de retorno de malloc es si tiene una estrategia para liberar suficiente memoria para permitir el programa para continuar corriendo Un mensaje informativo ayudará a diagnosticar el problema, pero solo si su programa causó la situación de falta de memoria. Por lo general, no es su programa y lo único que su programa puede hacer es salir lo más rápido posible.

assert(ptr != NULL); 

hará todas estas cosas. Mi estrategia habitual es tener una capa alrededor de Malloc que tenga en ella.

void *my_malloc(size_t size) 
{ 
    void *ptr = malloc (size); 
    assert(ptr != NULL); 
    return *ptr; 
} 

Luego llama a my_malloc en lugar de malloc. Durante el desarrollo utilizo una biblioteca de asignación de memoria que conduce a la depuración. Después de eso, si se queda sin memoria, recibo un mensaje.

+1

¡No olvide utilizar el 'my_free' correspondiente! De lo contrario, es un error;) –

7

Las asignaciones pueden fallar por varias razones. Lo que hace (y puede hacer) al respecto depende en parte de la falla de asignación.

Ser verdaderamente sin memoria es catastrófico. A menos que haya hecho un plan cuidadoso para esto, probablemente no haya nada que pueda hacer. (Por ejemplo, podría haber preasignado todos los recursos que necesitaría para guardar y cerrar la emergencia).

Pero muchas fallas de asignación no tienen nada que ver con la falta de memoria. La fragmentación puede hacer que falle una asignación porque no hay suficiente espacio contiguo disponible, aunque hay mucha memoria libre. La pregunta específicamente decía "estructura pequeña", por lo que probablemente sea tan mala como la verdadera condición de falta de memoria. (Pero el código cambia constantemente. ¿Qué es una estructura pequeña hoy podría ser un monstruo mañana. Y si es tan pequeño, realmente necesitas memoria del montón o puedes obtenerla de la pila?)

En un multi mundo enmarañado, las fallas de asignación a menudo son condiciones transitorias. Su asignación modesta podría fallar en este microsegundo, pero tal vez un hilo que acapara la memoria está a punto de liberar un gran búfer. Entonces, una estrategia de recuperación podría implicar un retraso y volver a intentarlo.

Cómo (y si) maneja la falla de asignación también puede depender del tipo de aplicación. Si está escribiendo un editor de documentos complejo, y un bloqueo significa perder el trabajo del usuario, entonces vale la pena gastar más esfuerzo para manejar estas fallas. Si su aplicación es transaccional y cada cambio se aplica incrementalmente al almacenamiento persistente, un bloqueo es solo un inconveniente menor para el usuario. Aun así, se debe considerar la tala. Si su aplicación recibe fallas de asignación rutinariamente, es probable que tenga un error y necesitará que los registros lo sepan y lo rastreen.

Por último, debes pensar en probar. Las fallas de asignación son poco frecuentes, por lo que la probabilidad de que se haya aplicado el código de recuperación en las pruebas es infinitamente pequeña, a menos que haya tomado medidas para garantizar la cobertura de las pruebas mediante el forzamiento artificial de las fallas. Si no va a probar su código de recuperación, probablemente no valga la pena escribirlo.

+0

+1: gracias por sus pensamientos bien articulados. – jldupont

Cuestiones relacionadas