Envolver llamadas a malloc () / realloc () … ¿es una buena idea?

Para una asignación, necesito asignar un búfer dynamic, usando malloc() para el búfer inicial y realloc() para expandir ese búfer si es necesario. En todos los lugares donde uso (re | m) alloc (), el código se parece a lo siguiente:

 char *buffer = malloc(size); if (buffer == NULL) { perror(); exit(EXIT_FAILURE); } 

El progtwig solo lee los datos de un archivo y los genera, por lo que pensé que simplemente salir del progtwig cuando falla (re | m) es una buena idea. Ahora, la verdadera pregunta es:

¿Sería beneficioso envolver las llamadas, por ejemplo, de esta manera?

 void *Malloc(int size) { void *buffer = malloc(size); if (buffer == NULL) { perror(); exit(EXIT_FAILURE); } return buffer; } 

¿O es una mala idea?

¿Debo molestarme en detectar errores OOM (sin memoria) en mi código C?

Esa es mi respuesta a una pregunta similar. En resumen, estoy a favor del diseño de aplicaciones para que se recuperen de cualquier tipo de fallas y luego traten la falta de memoria como una razón para fallar.

Es una mala idea en la forma presentada, porque en cualquier otra cosa que no sea ​​un progtwig trivial escrito para una tarea, quiere hacer algo más útil / elegante que rescatar. Así que mejor no meterse en malos hábitos. Esto no quiere decir que los envoltorios alrededor de la asignación son malos per se (centralizar el manejo de errores puede ser una buena cosa), solo que un envoltorio del cual no verifica el valor de retorno (por ejemplo, no se devuelve en absoluto en falla) es una mala idea, a menos que haya proporcionado algún mecanismo para permitir que el código se enganche en la lógica de rescate.

Si desea hacerlo en la forma que ha presentado, le recomiendo que utilice un nombre más claramente diferente a Malloc lugar de malloc . Como malloc_or_die . 🙂

En la mayoría de los casos, el bash de usar un puntero nulo bloqueará el progtwig lo suficientemente pronto de todos modos, y facilitará la depuración, ya que se obtiene un volcado de núcleo agradable en el que no se obtiene si se llama a exit() .

El único consejo que le daría es que elimine la referencia al puntero devuelto tan pronto como sea posible después de haberlo asignado, aunque solo sea de forma gratuita, para que el volcado de datos central pueda llevarlo directamente a la llamada malloc defectuosa.

Rara vez se puede hacer mucho para recuperarse del agotamiento de la memoria, por lo que salir es generalmente lo correcto. Pero hágalo de tal manera que facilite la autopsia.

Sin embargo, para que quede claro, el agotamiento de la memoria generalmente ocurre mucho después de que el sistema operativo se vea paralizado por la actividad de intercambio de páginas. Esta estrategia solo es realmente útil para capturar asignaciones ridículas, como intentar malloc (a_small_negative_number) debido a un error.

En tu caso está bien. Solo recuerde dar un mensaje con las razones de una salida prematura, y sería bueno especificar el número de línea. Algo así como esto:

 void* malloc2(int size, int line_num){ void *buffer = malloc(size); if (buffer == NULL) { printf("ERROR: cannot alloc for line %d\n", line_num); perror(); exit(EXIT_FAILURE); } return buffer; }; #define Malloc(n) malloc2((n), __LINE__) 

EDITAR: como han mencionado otros, no es un buen hábito para un progtwigdor experimentado, pero para un principiante que tiene dificultades incluso para mantener un seguimiento del flujo del progtwig en un caso “feliz” está bien.

Las ideas de que “verificar si hay errores en malloc son inútiles debido a un exceso de compromisos” o que “el sistema operativo ya estará paralizado para cuando malloc falle” están muy desactualizadas. Los sistemas operativos robustos nunca han comprometido demasiado la memoria, y los que no son tan sólidos históricamente (como Linux) hoy en día tienen formas fáciles de desactivar el compromiso y la protección contra el locking del sistema operativo debido al agotamiento de la memoria, siempre que las aplicaciones hagan su parte para no fallar. y quema cuando falla malloc !

Hay muchas razones por las que malloc puede fallar en un sistema moderno:

  • Recursos físicos insuficientes para instanciar la memoria.
  • Espacio de direcciones virtuales agotado, incluso cuando hay mucha memoria física libre. Esto puede suceder fácilmente en una máquina de 32 bits (o espacio de usuario de 32 bits) con> 4 gb de ram + swap.
  • Fragmentación de la memoria. Si sus patrones de asignación son muy malos, podría terminar con 4 millones de fragmentos de 16 bytes espaciados uniformemente con 1000 bytes, y no podrá satisfacer una llamada malloc(1024) .

Cómo lidiar con el agotamiento de la memoria depende de la naturaleza de su progtwig.

Ciertamente, desde el punto de vista de la salud del sistema en general, es bueno que su progtwig muera. Eso reduce la falta de recursos y puede permitir que otras aplicaciones sigan funcionando. Por otro lado, el usuario estará muy molesto si eso significa perder horas de trabajo editando un video, escribiendo un papel, redactando una publicación de blog, codificando, etc. O podrían sentirse felices si su reproductor de mp3 se muere repentinamente con algo fuera de línea. -memory significa que su disco se detiene y pueden volver a su procesador de textos y hacer clic en “guardar”.

En cuanto a la pregunta original de OP, recomendaría encarecidamente que no se escriban envoltorios malloc que mueran en caso de fallo, o que se escriba un código que simplemente asum que se producirá un error de seguridad inmediatamente después de usar el puntero nulo si el malloc falló. Es un mal hábito fácil de adquirir, y una vez que hayas escrito un código lleno de asignaciones no verificadas, será imposible volver a usar ese código en cualquier progtwig en el que la robustez sea importante.

Una solución mucho mejor es seguir devolviendo el error a la función de llamada, y dejar que la función de llamada devuelva el error a su función de llamada, etc., hasta que vuelva al estado main o similar, donde puede escribir if (failure) exit(1); . De esta manera, el código se puede reutilizar inmediatamente en otras situaciones en las que es posible que desee verificar errores y tomar algún tipo de pasos de recuperación para liberar memoria, guardar / volcar datos valiosos en el disco, etc.

Creo que es una mala idea, ya que, en primer lugar, comprobar el rendimiento de malloc no le permite comprar mucho en los sistemas modernos y, en segundo lugar, ya que esto le da una seguridad falsa de que cuando utiliza una llamada de este tipo, todas sus asignaciones están bien.

(Supongo que está escribiendo para un entorno alojado y no integrado, independiente).

Los sistemas modernos con un gran espacio de direcciones virtuales nunca devolverán (void*)0 de malloc o realloc aparte, tal vez, si los argumentos son falsos. Encontrará problemas mucho, mucho más tarde, cuando su sistema comience a intercambiarse como loco o incluso se quede sin intercambio.

Así que no, no compruebe el retorno de estas funciones, no tiene mucho sentido. En su lugar, verifique los argumentos de malloc contra 0 (y para realloc si ambos son 0 simultáneamente) con una afirmación, ya que el problema no está dentro de malloc o realloc sino en la forma en que los llama.