Wrong Way
El asombroso poder de C corta en ambos sentidos. Esto es lo que debe tener en cuenta y cómo mantener sus programas C en lĆ­nea recta y estrecha.

por Serdar Yegulalp

Pocos lenguajes de programación pueden igualar a C por pura velocidad y potencia a nivel de mĆ”quina. Esta afirmación era cierta hace 50 aƱos y sigue siendo verdad hoy. Sin embargo, hay una razón por la que los programadores acuƱaron el tĆ©rmino “pistolero” para describir el tipo de poder de C. Si no tiene cuidado, C puede volarle los dedos de los pies o los de otra persona.

Wrong Way
Wrong Way

Aquƭ hay cuatro de los errores mƔs comunes que puede cometer con C y cinco pasos que puede seguir para prevenirlos.

Error comĆŗn de C: no liberar la memoria malloc -ed (o liberarla mĆ”s de una vez)

Este es uno de los grandes errores en C, muchos de los cuales involucran la administración de la memoria. La memoria asignada (realizada con la malloc función) no se elimina automĆ”ticamente en C. Es el trabajo del programador deshacerse de esa memoria cuando ya no se usa. Si no libera solicitudes de memoria repetidas, terminarĆ” con una pĆ©rdida de memoria. Intente utilizar una región de la memoria que ya se haya liberado y su programa se bloquearĆ” o, lo que es peor, cojearĆ” y se volverĆ” vulnerable a un ataque mediante ese mecanismo.

Tenga en cuenta que una pĆ©rdida de memoria solo debe describir situaciones en las que se supone que la memoria debe liberarse, pero no lo estĆ”. Si un programa sigue asignando memoria porque la memoria es realmente necesaria y se usa para el trabajo, entonces su uso de la memoria puede ser  ineficiente , pero estrictamente hablando no es una fuga.

Error comĆŗn de C: leer una matriz fuera de los lĆ­mites

AquĆ­ tenemos otro de los errores mĆ”s comunes y peligrosos en C. Una lectura mĆ”s allĆ” del final de una matriz puede devolver datos basura. Una escritura mĆ”s allĆ” de los lĆ­mites de una matriz podrĆ­a corromper el estado del programa, bloquearlo por completo o, lo peor de todo, convertirse en un vector de ataque de malware.

Entonces, Āæpor quĆ© la carga de verificar los lĆ­mites de una matriz se deja al programador? En la especificación oficial de C, leer o escribir una matriz mĆ”s allĆ” de sus lĆ­mites es un “comportamiento indefinido”, lo que significa que la especificación no tiene voz en lo que se supone que debe suceder. El compilador ni siquiera estĆ” obligado a quejarse de ello.

C ha favorecido durante mucho tiempo dar energĆ­a al programador incluso bajo su propio riesgo. Una lectura o escritura fuera de lĆ­mites no suele ser atrapada por el compilador, a menos que habilite especĆ­ficamente las opciones del compilador para protegerse contra ella. Es mĆ”s, es posible que se exceda el lĆ­mite de una matriz en tiempo de ejecución de una manera que ni siquiera una verificación del compilador puede evitar.

Error C comĆŗn: no comprobar los resultados de malloc

malloc y calloc (para la memoria previamente puesta a cero) son las funciones de la biblioteca C que obtienen la memoria asignada al montón del sistema. Si no pueden asignar memoria, generan un error. En los dĆ­as en que las computadoras tenĆ­an relativamente poca memoria, existĆ­a una gran posibilidad de que una llamada malloc no tuviera Ć©xito.

A pesar de que las computadoras de hoy tienen gigabytes de RAM para distribuir malloc, siempre existe la posibilidad de que falle, especialmente bajo alta presión de memoria o cuando se asignan grandes bloques de memoria a la vez. Esto es especialmente cierto para los programas C que “asignan en bloque”, un gran bloque de memoria del sistema operativo primero y luego lo dividen para su propio uso. Si esa primera asignación falla porque es demasiado grande, es posible que pueda atrapar ese rechazo, reducir la asignación y ajustar la heurĆ­stica de uso de memoria del programa en consecuencia. Pero si la asignación de memoria falla sin trabas, todo el programa podrĆ­a fallar.

Error común de C: uso void* de punteros genéricos a la memoria

Usar void* para seƱalar la memoria es un hĆ”bito antiguo y malo. Los punteros a la memoria debe ser siempre char*, unsigned char* o uintpr t*. Los conjuntos de compiladores de C modernos deben proporcionar uintpr_t como parte de stdint.h

Cuando se etiqueta de una de estas formas, estĆ” claro que el puntero se refiere a una ubicación de memoria en abstracto en lugar de a algĆŗn tipo de objeto indefinido. Esto es doblemente importante si estĆ” realizando cĆ”lculos matemĆ”ticos con punteros. Con uintpr_t* y similares, el elemento de tamaƱo al que se apunta y cómo se utilizarĆ” no son ambiguos. Con void*, no mucho.

5 consejos para evitar errores comunes en C

¿Cómo se evitan estos errores tan comunes cuando se trabaja con memoria, matrices y punteros en C? Tenga en cuenta estos cinco consejos:

Estructura el programa en C para que la propiedad de la memoria se mantenga clara

Si recién estÔ iniciando una aplicación C, vale la pena pensar en la forma en que se asigna y libera la memoria como uno de los principios organizativos del programa. Si no estÔ claro dónde se libera una asignación de memoria determinada o bajo qué circunstancias, estÔ buscando problemas. Haga un esfuerzo adicional para que la propiedad de la memoria sea lo mÔs clara posible. Te harÔs un favor a ti mismo (y a los futuros desarrolladores).

Esta es la filosofía detrÔs de lenguajes como Rust. Rust hace que sea imposible escribir un programa que se compile correctamente a menos que exprese claramente cómo se posee y se transfiere la memoria. C no tiene tales restricciones, pero es aconsejable adoptar esa filosofía como guía siempre que sea posible.

Utilice las opciones del compilador de C que protegen contra problemas de memoria

Muchos de los problemas descritos en la primera mitad de este artĆ­culo se pueden marcar utilizando opciones estrictas del compilador. Las ediciones recientes de gcc, por ejemplo, proporcionan herramientas como AddressSanitizer (ā€œASANā€) como una opción de compilación para verificar errores comunes de administración de memoria.

Tenga cuidado, estas herramientas no captan absolutamente todo. Son barandillas; no agarran el volante si sales de la carretera. AdemÔs, algunas de estas herramientas, como ASAN, imponen costos de compilación y tiempo de ejecución, por lo que deben evitarse en las versiones de lanzamiento.

Utilice Cppcheck o Valgrind para analizar el código C en busca de pérdidas de memoria

Cuando los propios compiladores se quedan cortos, otras herramientas intervienen para llenar el vacío, especialmente cuando se trata de analizar el comportamiento del programa en tiempo de ejecución.

Cppcheck ejecuta un anÔlisis estÔtico en el código fuente de C para buscar errores comunes en la administración de la memoria y comportamientos indefinidos (entre otras cosas).

Valgrind proporciona un caché de herramientas para detectar errores de memoria y subprocesos en la ejecución de programas C. Esto es mucho mÔs poderoso que usar el anÔlisis en tiempo de compilación, ya que puede derivar información sobre el comportamiento del programa cuando estÔ realmente activo. La desventaja es que el programa se ejecuta a una fracción de su velocidad normal. Pero esto generalmente estÔ bien para probar.

Estas herramientas no son soluciones mÔgicas y no atraparÔn todo. Pero funcionan como parte de una estrategia defensiva general contra la mala gestión de la memoria en C.

Automatice la gestión de la memoria C con un recolector de basura

Dado que los errores de memoria son una fuente evidente de problemas de C, aquí hay una solución sencilla: no administre la memoria en C manualmente. Utilice un recolector de basura.

Sí, esto es posible en C. Puede usar algo como el recolector de basura Boehm-Demers-Weiser para agregar administración automÔtica de memoria a los programas C. Para algunos programas, el uso del colector Boehm puede incluso acelerar las cosas. Incluso se puede utilizar como mecanismo de detección de fugas.

La principal desventaja del recolector de basura Boehm es que no puede escanear ni liberar memoria que usa el malloc predeterminado. Utiliza su propia función de asignación, y solo funciona en la memoria que le asigna específicamente.

No uses C cuando otro idioma sea suficiente

Algunas personas escriben en C porque realmente lo disfrutan y lo encuentran fructífero. Sin embargo, en general, es mejor usar C solo cuando sea necesario, y luego con moderación, para las pocas situaciones en las que realmente es la opción ideal.

Si tiene un proyecto en el que el rendimiento de la ejecución se verÔ limitado principalmente por la E/S o el acceso al disco, es poco probable que escribirlo en C lo haga mÔs rÔpido de la manera que importa, y probablemente solo lo harÔ mÔs propenso a errores y difícil de escribir. mantener. El mismo programa bien podría estar escrito en Go o Python.

Otro enfoque es usar C solo para las partes de la aplicación que realmente requieren un alto rendimiento, y un lenguaje mÔs confiable, aunque mÔs lento, para otras partes. Nuevamente, Python se puede usar para empaquetar bibliotecas C o código C personalizado, lo que lo convierte en una buena opción para los componentes mÔs repetitivos como el manejo de opciones de línea de comandos.

Fuente: https://www.infoworld.com/article/3584362/4-common-c-programming-mistakes-and-5-tips-to-avoid-them.html

Deja una respuesta