Par exemple, l'outil SysInternals "FileMon" du passé a un pilote en mode noyau dont le code source est entièrement dans un fichier de 4 000 lignes. Il en va de même pour le tout premier programme de ping jamais écrit (~ 2 000 LOC).
L'utilisation de plusieurs fichiers nécessite toujours une surcharge administrative supplémentaire. Il faut configurer un script de construction et/ou un makefile avec des étapes de compilation et de liaison séparées, s'assurer que les dépendances entre les différents fichiers sont gérées correctement, écrire un script "Zip" pour une distribution plus facile du code source par e-mail ou par téléchargement, etc. sur. Les IDE modernes d'aujourd'hui prennent généralement beaucoup de ce fardeau, mais je suis à peu près sûr au moment où le premier programme de ping a été écrit, aucun tel IDE n'était disponible. Et pour les fichiers qui small as ~ 4000 LOC, sans un tel IDE qui gère bien plusieurs fichiers pour vous, le compromis entre les frais généraux mentionnés et les avantages de l'utilisation de plusieurs fichiers pourrait permettre aux gens de prendre une décision pour l'approche à fichier unique.
Parce que C n'est pas bon en modularisation. Cela devient compliqué (fichiers d'en-tête et #includes, fonctions externes, erreurs de temps de liaison, etc.) et plus vous apportez de modules, plus cela devient difficile.
Les langages plus modernes ont de meilleures capacités de modularisation en partie parce qu'ils ont appris des erreurs de C, et ils facilitent la décomposition de votre base de code en unités plus petites et plus simples. Mais avec C, il peut être avantageux d'éviter ou de minimiser tous ces problèmes, même si cela signifie regrouper ce qui serait autrement considéré comme trop de code dans un seul fichier.
Outre les raisons historiques, il y a une raison de l'utiliser dans les logiciels modernes sensibles aux performances. Lorsque tout le code est dans une seule unité de compilation, le compilateur est capable d'effectuer des optimisations de programme entier. Avec des unités de compilation distinctes, le compilateur ne peut pas optimiser l'ensemble du programme de certaines manières (par exemple en insérant certains codes).
L'éditeur de liens peut certainement effectuer certaines optimisations en plus de ce que le compilateur peut faire, mais pas toutes. Par exemple: les éditeurs de liens modernes sont vraiment bons pour éliminer les fonctions non référencées, même sur plusieurs fichiers objets. Ils peuvent être en mesure d'effectuer d'autres optimisations, mais rien de tel que ce qu'un compilateur peut faire à l'intérieur d'une fonction.
Un exemple bien connu d'un module de code source unique est SQLite. Vous pouvez en savoir plus à ce sujet sur la page The SQLite Amalgamation .
1. Résumé
Plus de 100 fichiers source distincts sont concaténés en un seul gros fichier de code C nommé "sqlite3.c" et appelé "la fusion". La fusion contient tout ce dont une application a besoin pour intégrer SQLite. Le fichier de fusion comprend plus de 180 000 lignes et une taille supérieure à 6 mégaoctets.
La combinaison de tout le code de SQLite en un seul gros fichier facilite le déploiement de SQLite - il n'y a qu'un seul fichier à suivre. Et parce que tout le code est dans une seule unité de traduction, les compilateurs peuvent faire une meilleure optimisation inter-procédures résultant en un code machine qui est entre 5% et 10% plus rapide.
En plus du facteur de simplicité mentionné par l'autre répondant, de nombreux programmes C sont écrits par une seule personne.
Lorsque vous avez une équipe de personnes, il devient souhaitable de diviser l'application sur plusieurs fichiers source pour éviter les conflits gratuits dans les modifications de code. Surtout quand des programmeurs avancés et très juniors travaillent sur le projet.
Quand une personne travaille seule, ce n'est pas un problème.
Personnellement, j'utilise plusieurs fichiers basés sur la fonction comme une chose habituelle. Mais c'est juste moi.
Parce que C89 n'avait pas de fonctions inline
. Ce qui signifiait que la division de votre fichier en fonctions entraînait la surcharge de pousser les valeurs sur la pile et de sauter. Cela a ajouté un peu de surcharge sur l'implémentation du code dans 1 grande instruction switch (boucle d'événement). Mais une boucle d'événement est toujours beaucoup plus difficile à mettre en œuvre efficacement (ou même correctement) qu'une solution plus modularisée. Ainsi, pour les projets de grande taille, les gens choisissent toujours de se modulariser. Mais quand ils ont réfléchi à l'avance à la conception et qu'ils pouvaient contrôler l'état en un seul interrupteur, ils ont opté pour cela.
De nos jours, même en C, il n'est pas nécessaire de sacrifier les performances pour modulariser car même en C, les fonctions peuvent être intégrées.
Cela compte comme un exemple d'évolution, qui, je m'étonne, n'a pas encore été mentionné.
Dans les jours sombres de la programmation, la compilation d'un seul FICHIER pouvait prendre quelques minutes. Si un programme était modularisé, l'inclusion des fichiers d'en-tête nécessaires (pas d'options d'en-tête précompilées) serait une cause supplémentaire importante de ralentissement. De plus, le compilateur peut choisir/devoir conserver certaines informations sur le disque lui-même, probablement sans l'avantage d'un fichier d'échange automatique.
Les habitudes que ces facteurs environnementaux ont amenées à se perpétuer dans les pratiques de développement en cours et ne se sont adaptées que lentement au fil du temps.
À l'époque, le gain de l'utilisation d'un seul fichier serait similaire à celui que nous obtenons en utilisant des SSD au lieu des disques durs.