Certains logiciels sont construits de cette façon.
Un exemple typique est SQLite . Il est parfois compilé comme un amalgamation (fait au moment de la construction à partir de nombreux fichiers sources).
Mais cette approche a des avantages et des inconvénients.
Évidemment, le temps de compilation augmentera considérablement. Ce n'est donc pratique que si vous compilez rarement ce genre de choses.
Peut-être que le compilateur pourrait optimiser un peu plus. Mais avec les optimisations au moment de la liaison (par exemple, si l'on utilise un fichier récent GCC, compiler et lier avec gcc -flto -O2
), vous pouvez obtenir le même effet (bien sûr, au prix d'une augmentation du temps de construction).
Je n'ai pas besoin d'écrire un fichier d'en-tête pour chaque fonction.
C'est une mauvaise approche (avoir un fichier d'en-tête par fonction). Pour un projet d'une seule personne (de moins de cent mille lignes de code, alias KLOC = kilo line of code ), il est tout à fait raisonnable - au moins pour les petits projets - d'avoir une simple fichier d'en-tête commun (que vous pourriez précompiler si vous utilisez CCG ), qui contiendra les déclarations de toutes les fonctions et de tous les types publics, et éventuellement définitions de static inline
fonctions (celles qui sont suffisamment petites et appelées assez fréquemment pour bénéficier de inlining ). Par exemple, le sash
coquille est organisé de cette manière (tout comme le lout
formateur avec 52 KLOC).
Vous pouvez également disposer de plusieurs fichiers d'en-tête, et peut-être d'un seul en-tête de "regroupement" qui #include
-s tous (et que vous pourriez précompiler). Voir par exemple jansson (qui a en fait un seul public ) et GTK (qui a lots d'en-têtes internes, mais la plupart des applications l'utilisant ont juste un #include <gtk/gtk.h>
qui comprennent à leur tour tous les en-têtes internes). De l'autre côté, POSIX a un grand nombre de fichiers d'en-tête, et il documente ceux qui doivent être inclus et dans quel ordre.
Certaines personnes préfèrent avoir beaucoup de fichiers d'en-tête (et d'autres préfèrent même mettre une seule déclaration de fonction dans son propre en-tête). Je ne le fais pas (pour les projets personnels, ou les petits projets sur lesquels seulement deux ou trois personnes commettent du code), mais il s'agit de goût . BTW, quand un projet se développe beaucoup, il arrive assez souvent que l'ensemble des fichiers d'en-tête (et des unités de traduction) change de manière significative. Regardez aussi dans REDIS (il a 139 .h
fichiers d'en-tête et 214 .c
c'est-à-dire des unités de traduction totalisant 126 KLOC).
Avoir un ou plusieurs unités de traduction est aussi une question de goût (et de commodité, d'habitudes et de conventions). Ma préférence va aux fichiers sources (c'est-à-dire aux unités de traduction) qui ne sont pas trop petits, typiquement plusieurs milliers de lignes chacun, et qui ont souvent (pour un petit projet de moins de 60 KLOC) un seul fichier d'en-tête commun. N'oubliez pas d'utiliser des construire l'automatisation outil comme GNU make (souvent avec un parallèle construire par make -j
; alors vous aurez plusieurs processus de compilation fonctionnant simultanément). L'avantage d'avoir une telle organisation du fichier source est que la compilation est raisonnablement rapide. D'ailleurs, dans certains cas, un métaprogrammation L'approche est intéressante : certains de vos fichiers "source" C (en-tête interne, ou unités de traduction) pourraient être généré par quelque chose d'autre (par exemple, un certain script en AWK ou un programme C spécialisé comme bison ou votre propre truc).
N'oubliez pas que le langage C a été conçu dans les années 1970, pour des ordinateurs beaucoup plus petits et plus lents que votre ordinateur portable préféré d'aujourd'hui (à l'époque, la mémoire était généralement d'un mégaoctet au maximum, voire de quelques centaines de kilooctets, et l'ordinateur était au moins mille fois plus lent que votre téléphone portable actuel).
Je vous conseille vivement de étudier le code source et construire quelques existant logiciel gratuit projets (par exemple, celles sur GitHub ou SourceForge ou votre distribution Linux préférée). Vous apprendrez qu'il s'agit d'approches différentes. N'oubliez pas que en C conventions et habitudes comptent beaucoup dans la pratique donc il y a différents façons d'organiser votre projet en .c
et .h
fichiers . Lisez à propos de la Préprocesseur C .
Cela signifie également que je ne dois pas inclure les bibliothèques standard dans chaque fichier que je crée.
Vous incluez les fichiers d'en-tête, pas les bibliothèques (mais vous devriez lien bibliothèques). Mais vous pourriez les inclure dans chaque .c
(et de nombreux projets le font), ou vous pouvez les inclure dans un seul en-tête et précompiler cet en-tête, ou vous pouvez avoir une douzaine d'en-têtes et les inclure après les en-têtes système dans chaque unité de compilation. YMMV. Notez que le temps de prétraitement est rapide sur les ordinateurs d'aujourd'hui (du moins, lorsque vous demandez au compilateur d'optimiser, puisque l'optimisation prend plus de temps que l'analyse syntaxique et le prétraitement).
Remarquez que ce qui va dans certains #include
Le fichier -d est conventionnel (et n'est pas défini par la spécification C). Certains programmes ont une partie de leur code dans un tel fichier (qui ne devrait alors pas être appelé "en-tête", mais simplement "fichier inclus", et qui ne devrait pas avoir de nom de fichier. .h
mais quelque chose d'autre comme .inc
). Regardez par exemple dans XPM dossiers. À l'autre extrême, vous pouvez en principe n'avoir aucun de vos propres fichiers d'en-tête (vous avez toujours besoin des fichiers d'en-tête de l'implémentation, par exemple <stdio.h>
ou <dlfcn.h>
de votre système POSIX) et copier et coller le code dupliqué dans votre .c
par exemple, avoir la ligne int foo(void);
dans chaque .c
mais c'est une très mauvaise pratique qui est mal vue. Cependant, certains programmes générant Des fichiers C partageant un contenu commun.
BTW, C ou C++14 n'ont pas de modules (comme OCaml). En d'autres termes, en C, un module est principalement une convention .
(remarquez que le fait d'avoir plusieurs milliers de très petit .h
et .c
de seulement quelques dizaines de lignes chacun peut ralentir votre temps de construction de façon spectaculaire ; avoir des centaines de fichiers de quelques centaines de lignes chacun est plus raisonnable, en terme de temps de construction).
Si vous commencez à travailler sur un projet solo en C, je vous suggère d'avoir d'abord un fichier d'en-tête (et de le précompiler) et plusieurs .c
unités de traduction. En pratique, vous changerez .c
beaucoup plus souvent que les fichiers .h
d'autres. Une fois que vous avez plus de 10 KLOC, vous pouvez les refactorer en plusieurs fichiers d'en-tête. Une telle refactorisation est délicate à concevoir, mais facile à faire (juste beaucoup de copier-coller de morceaux de codes). D'autres personnes auront des suggestions et des conseils différents (et c'est bien ainsi !). Mais n'oubliez pas d'activer tous les avertissements et les informations de débogage lors de la compilation (donc compilez avec le bouton gcc -Wall -g
peut-être en fixant CFLAGS= -Wall -g
dans votre Makefile
). Utilisez le gdb
débogueur (et Valgrind ...). Demandez des optimisations ( -O2
) lorsque vous évaluez un programme déjà débogué. Utilisez également un système de contrôle de version comme Git .
Au contraire, si vous êtes en train de concevoir un projet plus vaste sur lequel plusieurs personnes fonctionnerait, il pourrait être préférable d'avoir plusieurs fichiers - voire plusieurs fichiers d'en-tête - (intuitivement, chaque fichier a une seule personne qui en est principalement responsable, d'autres personnes apportant des contributions mineures à ce fichier).
Dans un commentaire, vous ajoutez :
Je parle d'écrire mon code dans plusieurs fichiers différents et d'utiliser un Makefile pour les concaténer.
Je ne vois pas pourquoi cela serait utile (sauf dans des cas très bizarres). Il est bien mieux (et très habituel et courant) de compiler chaque unité de traduction (par exemple chaque .c
) dans son fichier objet (a .o
ELF sous Linux) et lien plus tard. C'est facile avec make
(en pratique, lorsque vous ne modifiez qu'un seul .c
par exemple pour corriger un bogue, seul ce fichier est compilé et la compilation incrémentale est très rapide), et vous pouvez lui demander de compiler les fichiers objets dans le répertoire parallèle en utilisant make -j
(et alors votre construction va très vite sur votre processeur multi-core).