Je suis un débutant relatif à C et j'ai besoin d'apprendre comment fonctionnent les fichiers makefile et je suis un peu confus sur la façon dont fonctionne la combinaison des fichiers C. Disons que nous avons un main.c, un foo.c, et un bar.c. Comment le code doit-il être écrit pour que main.c reconnaisse les fonctions dans les autres fichiers? En outre, dans foo.c et bar.c, tout le code est-il écrit dans la fonction principale ou avons-nous besoin d'écrire d'autres fonctions pour ce que nous avons besoin de faire? J'ai lu des tutoriels sur la façon dont les fichiers sont écrits, et cela a du sens pour la plupart, mais je suis encore un peu confus quant à la logistique de base.Plusieurs fichiers sources dans C- Comment fonctionnent exactement les fichiers makefile?
Répondre
En règle générale, vous allez définir vos fonctions pour les autres fichiers dans un fichier d'en-tête, qui peut ensuite être inclus dans main.c. Par exemple, tenez compte des extraits:
main.c:
#include "foo.h"
int main(int argc, char *argv[]) {
do_foo();
return 0;
}
foo.h:
void do_foo();
foo.c:
#include <stdio.h>
#include "foo.h"
void do_foo() {
printf("foo was done\n");
}
Ce qui va arriver que principale .c sera transformé en fichier objet (main.o), et foo.c sera transformé en fichier objet (foo.o). Ensuite, l'éditeur de liens reliera ces deux fichiers ensemble et c'est là que la fonction do_foo()
dans main.c est 'associée' à la fonction dans foo.o.
Exemple commande GCC: gcc -o myprogram main.c foo.c
Exemple makefile
myprogam: main.o foo.o
gcc -o myprogram main.o foo.o
main.o: main.c foo.h
gcc -c main.c
foo.o: foo.c foo.h
gcc -c foo.c
Mettre en retrait le fichier Makefile avec les véritables onglets comme indiqué ici http://stackoverflow.com/a/9580615/1461060 – gihanchanuka
Les fonctions foo.c
qui doivent être appelées de l'extérieur foo.c
doivent avoir des prototypes dans foo.h
. Les fichiers externes qui doivent appeler ces fonctions doivent alors #include "foo.h"
. foo.c
et bar.c
ne devrait même pas avoir une fonction main()
si elles font partie du même programme que main.c
.
Les Makefiles définissent des cibles. Pour les programmes simples, vous pouvez simplement avoir une seule cible qui compile le tout. Des programmes plus complexes (lire: plus grand) peuvent avoir des cibles intermédiaires (comme foo.o) qui permettront d'éviter une recompilation inutile. La façon dont make
détermine si une cible donnée doit être recompilée ou non est de regarder les temps de modification de tous les prérequis (les choses après les deux points) et si l'un d'entre eux vient après le dernier temps modifié du fichier cible lui-même, se reconstruit.
Voici un exemple très simple:
main.c:
#include "foo.h"
int main()
{
fooprint(12);
return 0;
}
foo.c:
#include "stdio.h"
#include "foo.h"
void fooprint(int val)
{
printf("A value: %d\n", val);
}
foo.h:
void fooprint(int val);
Makefile:
main: main.c foo.o
gcc -o main main.c foo.o
foo.o: foo.c
gcc -c foo.c
Ensuite, vous pouvez exécuter make main
et il compiler foo.c
dans foo.o
puis compiler main.c et le lien avec foo.o. Si vous modifiez main.c
, il va simplement recompiler main.c
et le lier avec le foo.o
déjà construit.
Votre première phrase devrait-elle lire «Les fonctions de foo.c qui doivent être appelées de l'extérieur de foo.c devraient avoir des prototypes dans foo.h», en omettant le «ne pas» et en inversant ce que vous dites réellement? Les corollaires sont (1) les fonctions qui n'ont pas besoin d'être appelées de l'extérieur foo.c devraient être définies comme des fonctions statiques, et (2) en parallèle, les fonctions de bar.c qui doivent être appelées de l'extérieur bar.c devraient avoir des prototypes dans bar.h (et les fonctions à l'intérieur de bar.c qui ne devraient pas être appelées depuis l'extérieur de bar.c devraient être définies comme des fonctions statiques). –
@Jonathan: Oups, oui. J'allais faire valoir ce point à propos de static, mais j'ai décidé de ne pas le faire. – nmichaels
Le PO pose au moins autant de questions sur l'organisation du programme que sur les fichiers makefiles; Je crois que l'éducation précoce sur «statique devrait être défaut» est une bonne idée - tout ce qui peut être statique devrait être. L'autre erreur habituelle du débutant est 'toutes les déclarations utilisées dans foo.c devraient être dans foo.h', plutôt que d'indiquer que 'foo.h indique au monde extérieur ce dont ils ont besoin pour utiliser les fonctions que foo.c définit pour eux utiliser'. Par exemple, les types utilisés uniquement dans foo.c ne doivent pas être définis dans foo.h - mais ils le sont si souvent. –
A propos de C/C++ compilation
Lorsque vous avez un ensemble de fichiers, vous faites généralement 2 choses:
- compilez chaque fichier source (.c) dans un fichier objet (.o)
- liez tous les fichiers objets dans un fichier exécutable.
Les fichiers source sont indépendants - dont vous avez besoin fichiers d'en-tête pour être en mesure de fournir « l'information » (déclarations) sur les fonctions dans un module donné afin de laisser tout autre module les utiliser. Les fichiers d'en-tête ne sont pas compilés par eux-mêmes - ils sont #include
d en tant que parties de fichiers source.
Jetez un oeil ci-dessous sur la façon dont les commandes ressemblent et comment elles sont gérées par make
.
A propos makefiles
Makefile est un ensemble d'objectifs et de règles pour les construire. Un cible est "quelque chose qui peut être construit et aboutit à un fichier donné". (Il existe aussi des cibles "bidon" qui ne résultent pas en un fichier et sont simplement là pour exécuter des commandes - une commune est appelée clean
pour supprimer les résultats de la compilation).
Chaque cible a 2 parties:
- liste des dépendances, "liste de sensibilité" (d'autres fichiers et cibles qui sont nécessaires pour cet objectif) (après
:
, séparés par des virgules), - liste des commandes shell qui sont exécutées pour générer cette cible (ci-dessous, en retrait)
Considérons cet exemple:
main: main.o module1.o module2.o
g++ main.o module1.o module2.o -o main
Cela signifie que: « Pour construire le fichier main
, je dois d'abord faire en sorte que cible les main.o
, module1.o
et module2.o
sont à jour; alors j'ai besoin d'appeler la commande suivante ... ".
Cela peut aussi être réécrite comme:
main: main.o module1.o module2.o
gcc $^ -o [email protected]
Les variables (tout en commençant par $
est une variable) sera étendue à la liste des dépendances et le nom de la cible, comme prévu.
Vous pouvez définir vos propres variables et les développer comme suit:
OBJS = main.o module1.o module2.o
main: $(OBJS)
# code goes here
Vous compilez des unités individuelles de traduction comme suit:
main.o: main.c
gcc -c $< -o [email protected]
# note the -c option, which means: "compile, but don't link"
# $< will expand to the first source file
Vous pouvez ajouter des dépendances d'en-tête pour reconstruire main.o lorsque le fichier main.c ou l'un de ses en-têtes change:
main.o: main.c module1.h module2.h
gcc -c $< -o [email protected]
Afin de ne pas écrire la même commande encore et encore, vous pouvez définir une règle générale et juste fournir les dépendances (si vous voulez):
%.o: %.c
gcc -c $< -o [email protected]
main.o: main.c module1.h module2.h
module1.o: module1.c module1.h module2.h
Il y a aussi un peu de magie pour générer le dependencies automatically (see link) . L'un des inconvénients de l'utilisation de Make est qu'il ne le fait pas tout seul (comme le font certains systèmes de construction - comme les SCons que je préfère pour C/C++).
Le rendu a peu à voir avec la structure d'un programme C. Tout make fait est de définir un arbre de dépendance et d'exécuter des commandes lorsqu'il trouve que les dépendances sont détraquées. Mon dicton, dans un makefile:
foo.exe : foo.c bar.c baz.c
sez simplement: foo.exe dépend foo.c, bar.c et baz.c. Ce, sotto vocce, se développé, en utilisant le jeu de règles par défaut de faire, à quelque chose comme:
foo.exe : foo.obj bar.obj baz.obj
foo.obj : foo.c
bar.obj : bar.c
baz.obj : baz.c
faire des promenades simplement l'arbre de dépendance à partir de sa racine (dans ce cas, foo.exe). Si une cible n'existe pas ou si l'un des objets dont elle dépend est plus récent que la cible, les commandes associées sont exécutées. pour rendre la dépendance correcte.
Voir Managing Projects with Make de O'Reilly pour plus que ce que vous voulez probablement savoir.
En ce qui concerne la deuxième partie de votre question, la réponse est juste deux lettres: K and R. Leur The C Programming Language est sans doute l'un des meilleurs livres de programmation informatique jamais écrit.
Essentiellement un makefile comprend des règles de la forme:
<this file> : <needs these files>
<and is created by this command>
Vous avez normalement au moins une cible de haut niveau, si l'une de ses dépendances n'existent pas, faire cherche un règle qui a ce fichier en tant que cible.Il le fait récursivement jusqu'à ce qu'il ait résolu toutes les dépendances de la cible de niveau supérieur, avant d'exécuter la commande de niveau supérieur (s'il y en a une - les deux dépendances et la commande sont des champs facultatifs dans une règle)
Un fichier make peut avoir 'règles par défaut' basées sur des modèles, et il y a des macros intégrées pour divers scénarios de correspondance de fichiers ainsi que des macros de définition d'utilisateur et l'inclusion de fichiers make imbriqués. J'ai simplifié le formulaire de règle ci-dessus dans le cas le plus habituel. En fait, la commande n'a pas besoin de créer la cible, c'est simplement une commande à exécuter une fois que tous les fichiers de la dépendance sont présents. De plus, la cible n'a pas besoin d'être un fichier non plus. Souvent, la cible de niveau supérieur est une cible «factice» appelée «tout» ou similaire.
Il y a bien sûr beaucoup de subtilités et de nuances à faire, toutes détaillées dans the manual (GNU make spécifiquement, il y a d'autres utilitaires de make).
Comment fonctionne Makefile?
=> Lorsque la commande make est exécutée sur le terminal, elle recherche un fichier nommé makefile ou Makefile dans le répertoire courant et construit une arborescence de dépendances.
Si vous avez plusieurs Makefile, vous pouvez exécuter la commande spécifique:
make -f MyMakefile
=> Sur la base de faire cible spécifiée dans makefile, effectuer des contrôles si les fichiers de dépendance de l'existence cible. Et s'ils existent, qu'ils soient plus récents que la cible elle-même, en comparant les horodatages des fichiers.
Here our first and default target is “all” which looks for main.o and function.o file dependencies. Second and third target is main.o and function.o respectively which have dependencies of main.c and function.c respectively.
=> Avant d'exécuter les commandes de cible correspondant, ses dépendances doivent être respectées, quand ils ne sont pas atteints, les objectifs de ces dépendances sont exécutées avant que la cible de marque donnée, pour fournir les dépendances manquantes.
=> Lorsqu'une cible est un nom de fichier, make compare les horodatages du fichier cible et de ses fichiers de dépendance. Si le fichier de dépendance est plus récent que le fichier cible, la cible s'exécutera sinon ne s'exécutera pas.
In our case, when first target “all” start executing it looks for main.o file dependency, if its not met. Then it goes to second target main.o which check for its dependency main.c and compare time-stamp with it. If target found main.c dependency is updated, then target execute else not. Same process is follow for next target function.o.
=> Il finit ainsi par vérifier de manière récursive tout le long de l'arbre des dépendances jusqu'aux fichiers de code source. Grâce à ce processus, gagner du temps, en exécutant uniquement les commandes qui doivent être exécutées, en fonction des fichiers sources (répertoriés comme dépendances) qui ont été mis à jour et dont l'horodatage est plus récent que leur cible. Maintenant, quand une cible n'est pas un nom de fichier (que nous appelons "cibles spéciales"), make ne peut évidemment pas comparer les horodatages pour vérifier si les dépendances de la cible sont plus récentes. Par conséquent, une telle cible est toujours exécutée.
In our Makefile, special targets are “all” and “clean”. As we discussed target “all” earlier, but we not discuss target clean. Target clean removes the all object files created during compilation and binary executable files according to command.
Pour l'exécution de chaque cible, imprimez les actions lors de leur exécution. Notez que chacune des commandes est exécutée dans un environnement de sous-shell séparé en raison d'une exécution sécurisée, de sorte qu'elles ne peuvent pas modifier l'environnement de shell actuel, ce qui peut affecter l'exécution d'une autre cible. Par exemple, si une commande contient cd newdir, le répertoire courant ne sera modifié que pour cette commande de ligne, pour la commande de ligne suivante, le répertoire courant sera inchangé.
Source: - http://www.firmcodes.com/linux/write-first-makefile-c-source-code-linux-tutorial/
- 1. Makefile compilation plusieurs fois les mêmes sources
- 2. Comment utiliser un makefile exactement?
- 3. Plusieurs sources de message dans les fichiers de configuration Spring
- 4. Comment fonctionnent les fichiers kml?
- 5. Comment fonctionnent exactement les interfaces dans Go?
- 6. Makefile avec plusieurs cibles
- 7. Compilez plusieurs fichiers C avec make
- 8. Comment lire plusieurs fichiers dans C?
- 9. C - Lire plusieurs fichiers
- 10. Sources de sous-répertoires dans Makefile
- 11. WIX 3: Impossible d'ouvrir les fichiers sources
- 12. Comment fusionner plusieurs fichiers PDB?
- 13. xcode duplique les fichiers sources de construction
- 14. Compilng fichiers makefile C/C++ sous Windows (XP)
- 15. Comment avoir des fichiers CVS dans un répertoire différent de celui des fichiers sources dans NetBeans?
- 16. Comment déplacez-vous plusieurs fichiers dans git?
- 17. Comment fonctionnent les références de fichiers dans un objet PHP?
- 18. Fichiers .sbr dans le contrôle des sources
- 19. Makefile pour placer les fichiers objets des fichiers source différents répertoires dans un seul répertoire séparé?
- 20. Spécifier le répertoire pour les fichiers objet C++ et l'exécutable dans Makefile
- 21. Comment définir plusieurs chemin d'inclusion dans Makefile
- 22. makefile NMAKE, reliant les fichiers objets dans un sous-dossier
- 23. Makefile et fichiers sous un répertoire
- 24. Les téléchargements de fichiers ne fonctionnent pas
- 25. comment fonctionnent les fichiers obj une fois chargé
- 26. Recherche de plusieurs chaînes dans plusieurs fichiers
- 27. C#: gérer plusieurs fichiers App.config
- 28. Importation de fichiers sources de bibliothèques tierces dans Eclipse CDT
- 29. Makefile option/règle pour gérer les fichiers source manquants/supprimés
- 30. Séparation d'objets et de sources avec un fichier makefile
Juste une remarque, pas une réponse à part entière: Vous ne voulez pas vraiment avoir une fonction 'main' dans plus d'un fichier qui sera compilé dans la même cible, comme alors vous aurez deux fonctions avec le même nom. –