¿Existen pautas comúnmente aceptadas sobre cómo escribir la C moderna?

13

Tengo un sólido historial de Java / Groovy y me asignaron a un equipo que mantiene una base de código C bastante grande para un software administrativo.

Algunos puntos problemáticos, como tratar con blob en la base de datos o generar informes en PDF y Excel, se han externalizado al servicio web java.

Sin embargo, como desarrollador de Java, estoy un poco confundido por algunos aspectos del código:

  • es detallado (especialmente cuando se trata de 'excepción')
  • hay muchos métodos enormes (muchos más de 2000 líneas de método)
  • no hay estructuras de datos avanzadas (echo mucho de menos la Lista, el Conjunto y el Mapa)
  • ninguna separación de interés (SQL se mezcla alegremente en todo el código)

Como resultado, siento que el negocio está oculto en toneladas de código técnico y mi cerebro, en forma de Objeto Orientado y una pizca de Programación Funcional, no está a gusto.

El lado bueno del proyecto es que el código es sencillo: no hay marco, no hay manipulación de código de byte en tiempo de ejecución, no AOP. Y el servidor puede responder simultáneamente a más de 10000 usuarios con una sola máquina usando menos memoria de la que necesita Java para escupir "hola mundo".

Quiero aprender cómo escribir código C de acuerdo con los principios modernos comúnmente aceptados. ¿Hay algún principio comúnmente aceptado sobre cómo debería escribirse y estructurarse la C moderna?

Algo un poco como el equivalente al libro 'Eficaz Java', pero para C.

Edite a la luz de las respuestas y comentarios:

  • Intentaré adaptar mi mentalidad al código C y no intentar reflejarlo en OOP.
  • Empecé a escanear y leer las guías de estilo de codificación recomendadas del comentario (los Estándares de codificación de GNU y el Estilo de codificación de kernel de Linux).
  • Intentaré proponer este estilo de código a mis compañeros de trabajo. La parte más difícil podría ser convencer a los compañeros de trabajo de que un método enorme podría dividirse en partes más pequeñas y que repetir el mismo código de manejo de errores de 4 líneas podría evitarse con la ayuda de un método.
pregunta Guillaume 03.11.2016 - 14:04

4 respuestas

14

Puedo leer de su pregunta que el problema no es que el código sea antiguo C, sino simplemente malo en la programación. La mayoría de los problemas que mencionó como verbosidad, enormes funciones de más de 2000 líneas o ninguna separación de preocupaciones es aplicable a cualquier lenguaje, C o Java por igual.

La verbosidad se mencionó en el contexto del manejo de errores. No proporcionó un ejemplo, así que solo puedo recordar que el código de manejo de errores también es código . No hay excusa para las secciones repetitivas del código repetitivo. Factoriza hacia fuera; ya sea para una función o (si no vale la pena crear una función separada) realice el patrón goto Error; y mueva el manejo de errores y la limpieza de recursos a una sección Error: en la parte inferior de la función.

Si parece que el problema es pasar el error por la cadena de llamadas, pregúntese: ¿la función allí arriba realmente necesita saber que un pequeño individuo aquí abajo tuvo algún problema? Los mecanismos de excepción incorporados en un lenguaje facilitan la tarea, pero en general es mejor manejar las excepciones antes (en cualquier idioma) para que la condición de error no contamine la lógica del código de alto nivel. Y si la función allí arriba realmente necesita saber, hay formas de emular excepciones con setjmp y longjmp .

Creo que el único problema realmente relacionado con C que se menciona es la falta de contenedores estándar. Mientras que Set en general se puede reemplazar con una matriz ordenada y Map (en su mayor parte) con una matriz de pares o un struct (si conoce la clave configurada previamente, map[key] = value se convierte en s.key = value ), pero el hecho es que no hay un contenedor de matriz dinámica en la biblioteca estándar. En C99, al menos puede declarar una matriz de longitud variable en la pila ( int array[len] ), pero necesita calcular len de antemano (generalmente no es difícil) y, por supuesto, no puede devolverla como cualquier objeto asignado a la pila. La mayoría de los proyectos terminan escribiendo su propio contenedor de matriz dinámica o adoptando uno de código abierto.

En una nota de cierre, me gustaría señalar que he estado allí. He sido el programador de Java que se trasladó a C ++ y C. puro. Me gustaría recomendar "leer el libro X para aprender una buena C", pero no hay ninguna, como no hay ninguna para Java. El camino a seguir es absorber todas las complejidades del lenguaje y la biblioteca estándar; google mucho, lee mucho y codifica mucho hasta que comiences a pensar en C. Intentar escribir cosas en C como lo harías en Java es tan frustrante como intentar escribir una oración en un idioma extranjero con palabras directamente traducidas de su lengua materna; tanto usted como el lector se estremecerán. La buena noticia es que aprender bien la programación es lento, pero aprender otro idioma es rápido. así que si escribes código decente en Java, aplicar el mismo sentido común a C también funcionará.

    
respondido por el An Owl 05.11.2016 - 10:01
7
  

El lado bueno del proyecto es que el código es sencillo:   no hay marco, ni manipulación de código de byte en tiempo de ejecución, ni AOP.   Y el servidor puede responder simultáneamente a más de 10000 usuarios con una sola   La máquina utiliza menos memoria de la que necesita Java para escupir "hola mundo".

Le recomendaría que sea cauteloso sobre si vale la pena su tiempo y el dinero de la compañía para gastar recursos en "modernizar" un software que funcione con poca complejidad de código y que tenga un buen desempeño. Existe una alta probabilidad de que usted mismo introduzca nuevos errores, especialmente porque parece ser un sistema con el que no está familiarizado.

Si aún desea seguir esa ruta, le sugeriría lo siguiente:

  • Haga (o genere) un diagrama de estado del software / código
  • Sumérjase en el código y haga una lista de las partes más complejas o críticas del código respectivamente
  • Encuentre a alguien que tenga conocimiento sobre ese código base y pregúntele por qué se ha creado de esta manera y qué se sabe que causa problemas.
  • Escribe documentación de lo que has aprendido

En este punto, decidirás si vale la pena explorar esto o no. Si la cultura de su empresa no recompensa el fracaso, obtenga la luz verde de un administrador superior.

  • Compartimentalizar los diferentes componentes del software y escribir pruebas unitarias para cada uno.
  • Iterar hasta que puedas pegar los diferentes módulos juntos
  • Realice pruebas adicionales que simulen la interacción real del usuario (pruebas de estrés, etc.)

Creo que es un buen mapa de carreteras y te llevará a donde necesites. Sin conocer los detalles de este proyecto, es difícil ayudarlo mucho. Por favor, no descarte mi descargo de responsabilidad como demasiado alarmista. Toneladas de excelentes programadores han golpeado el polvo al intentar reescribir un proyecto existente a su lenguaje favorito o al usar herramientas "modernas". Esa es una decisión que debe ser cuidadosamente pensada y le insto a que no se desvíe de la cuenta y lo haga por su cuenta sin el apoyo de la gerencia o la ayuda de sus colegas.

    
respondido por el Jerry Dean 03.11.2016 - 22:06
2

Si prefiere un lenguaje de nivel superior, hay algunos lenguajes como C ++ o Objective-C que se pueden mezclar con el código C con bastante facilidad.

Alternativamente, C y C ++ son razonablemente compatibles. Es posible que pueda simplemente compilar todo el código base como C ++ con pocos cambios; tendrá la variable ocasional llamada "clase" o "plantilla" que necesita cambiar de nombre, pero en la práctica eso será todo. (sizeof ('a') es diferente en C y C ++, pero creo que nunca lo he usado).

Si vas por esa ruta, considera que el siguiente mantenedor podría no ser demasiado fluido con C ++. No te dejes llevar. Aproveche C ++, pero solo hasta el momento en que un programador de C pueda entenderlo fácilmente.

    
respondido por el gnasher729 05.11.2016 - 23:16
1

Básicamente, escribir un buen código C es lo mismo que escribir un buen código C ++ o Java: quieres una clase, usa un struct . Desea herencia, incluya la base struct como primer miembro sin nombre. Desea funciones virtuales, agregue un puntero a un struct estático de punteros de función. Y así sucesivamente, etc. Es exactamente lo que C ++ hace bajo el capó, la única diferencia es que está explícito en C. Por lo tanto, puede hacer una programación perfectamente orientada a objetos en C, solo se ve un poco diferente y más placentera de lo que usted están acostumbrados a.

El punto es que la buena programación tiene que ver con paradigmas, no con características de lenguaje. Es cierto que siempre es bueno que las funciones de idioma proporcionen un buen soporte para los paradigmas que desea utilizar, pero las funciones de idioma no son un requisito. Una vez que te das cuenta de esto, puedes escribir un buen código en casi cualquier idioma (aparte de algunos lenguajes esotéricos como brainfuck o INTERCAL).

Por supuesto, el problema sigue siendo que la biblioteca estándar de C no contiene ninguna de esas clases de contenedores ingeniosas a las que está acostumbrado. Desafortunadamente, eso significa que tendrá que usar el suyo propio o solucionar este problema mediante el uso de matrices asignadas dinámicamente. Pero apostaría a que pronto descubrirá que todo lo que realmente necesita son matrices dinámicas ( malloc() ) y listas / árboles vinculados que se implementan a través de miembros punteros dentro de sus clases.

    
respondido por el cmaster 11.11.2016 - 15:15

Lea otras preguntas en las etiquetas