Por ejemplo, la herramienta SysInternals "FileMon" del pasado tiene un controlador en modo kernel cuyo código fuente es completamente en un archivo de 4,000 líneas. Lo mismo para el primer programa de ping jamás escrito (~ 2,000 LOC).
Por ejemplo, la herramienta SysInternals "FileMon" del pasado tiene un controlador en modo kernel cuyo código fuente es completamente en un archivo de 4,000 líneas. Lo mismo para el primer programa de ping jamás escrito (~ 2,000 LOC).
El uso de varios archivos siempre requiere una sobrecarga administrativa adicional. Uno tiene que configurar un script de compilación y / o makefile con etapas separadas de compilación y enlace, asegurarse de que las dependencias entre los diferentes archivos se manejen correctamente, escribir un script "zip" para facilitar la distribución del código fuente por correo electrónico o descargar, y así en. Los IDE modernos de hoy típicamente toman mucho de esa carga, pero estoy bastante seguro de que en el momento en que se escribió el primer programa de ping, no estaba disponible tal IDE. Y para los archivos que son pequeños como ~ 4000 LOC, sin un IDE que administre bien varios archivos, la compensación entre la sobrecarga mencionada y los beneficios de usar varios archivos podrían permitir que las personas tomen una decisión. el enfoque de archivo único.
Porque C no es bueno en la modularización. Se complica (los archivos de encabezado y #incluye, las funciones externas, los errores de tiempo de enlace, etc.) y cuantos más módulos traiga, más difícil será.
Los lenguajes más modernos tienen mejores capacidades de modularización en parte porque aprendieron de los errores de C y hacen que sea más fácil dividir su base de código en unidades más pequeñas y simples. Pero con C, puede ser beneficioso evitar o minimizar todos esos problemas, incluso si eso significa agrupar lo que de otra manera se consideraría demasiado código en un solo archivo.
Aparte de las razones históricas, hay una razón para usar esto en el software moderno sensible al rendimiento. Cuando todo el código está en una unidad de compilación, el compilador puede realizar optimizaciones de todo el programa. Con unidades de compilación separadas, el compilador no puede optimizar todo el programa de ciertas maneras (por ejemplo, al insertar cierto código).
El enlazador puede realizar algunas optimizaciones además de lo que puede hacer el compilador, pero no todas. Por ejemplo: los enlazadores modernos son realmente buenos para eludir funciones no referenciadas, incluso a través de múltiples archivos de objetos. Es posible que puedan realizar otras optimizaciones, pero nada como lo que puede hacer un compilador dentro de una función.
Un ejemplo bien conocido de un módulo de código de una sola fuente es SQLite. Puede leer más sobre esto en la página The SQLite Amalgamation .
1. Resumen ejecutivo
Más de 100 archivos de origen separados se concatenan en una sola gran archivos de código C denominados "sqlite3.c" y denominados "la fusión". los amalgamation contiene todo lo que una aplicación necesita para incrustar SQLite. El archivo de fusión tiene más de 180,000 líneas y más de 6 megabytes de tamaño.
La combinación de todo el código para SQLite en un archivo grande hace que SQLite más fácil de implementar: solo hay un archivo para realizar un seguimiento. Y Debido a que todo el código está en una sola unidad de traducción, los compiladores pueden hacerlo Una mejor optimización entre procedimientos que resulta en un código de máquina que es entre un 5% y un 10% más rápido.
Además del factor de simplicidad que mencionó el otro encuestado, muchos programas de C están escritos por un individuo.
Cuando tiene un equipo de personas, es conveniente dividir la aplicación en varios archivos de origen para evitar conflictos gratuitos en los cambios de código. Especialmente cuando hay programadores avanzados y muy pequeños trabajando en el proyecto.
Cuando una persona trabaja sola, eso no es un problema.
Personalmente, uso varios archivos basados en la función como algo habitual. Pero eso es solo yo.
Porque C89 no tenía funciones inline
. Lo que significaba que dividir el archivo en funciones causaba la sobrecarga de empujar valores en la pila y saltar. Esto agregó un poco de sobrecarga sobre la implementación del código en 1 instrucción de conmutación grande (bucle de eventos). Pero un bucle de eventos siempre es mucho más difícil de implementar de manera eficiente (o incluso correcta) que una solución más modular. Entonces, para proyectos de gran tamaño, las personas todavía optarían por modularizar. Pero cuando tenían el diseño pensado de antemano y podían controlar el estado en una instrucción de cambio, optaron por eso.
Hoy en día, incluso en C, no es necesario sacrificar el rendimiento para modularizar porque incluso en C las funciones pueden estar integradas.
Esto cuenta como un ejemplo de evolución, y me sorprende que aún no se haya mencionado.
En los días oscuros de la programación, la compilación de un solo ARCHIVO podría tomar minutos. Si un programa se modularizara, la inclusión de los archivos de encabezado necesarios (sin opciones de encabezado precompilados) sería una causa adicional importante de desaceleración. Además, el compilador podría elegir / necesitar mantener cierta información en el propio disco, probablemente sin el beneficio de un archivo de intercambio automático.
Los hábitos que estos factores ambientales llevaron a las prácticas de desarrollo en curso y se han ido adaptando lentamente con el tiempo.
En el momento en que la ganancia de usar un solo archivo sería similar a la que obtenemos mediante el uso de SSD en lugar de HDD.
Lea otras preguntas en las etiquetas design c source-code