2010-12-03 5 views
20

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?

+1

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. –

Répondre

24

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 
+2

Mettre en retrait le fichier Makefile avec les véritables onglets comme indiqué ici http://stackoverflow.com/a/9580615/1461060 – gihanchanuka

2

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.

+0

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). –

+0

@Jonathan: Oups, oui. J'allais faire valoir ce point à propos de static, mais j'ai décidé de ne pas le faire. – nmichaels

+0

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. –

13

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++).

1

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.

alt text

2

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).

1

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/

Questions connexes