Je suis actuellement sur un projet de cours d'un émulateur du micro-processeur MIPS, programmé en C.
Vu que j'utilise de nombreuses fonctions, j'ai besoin de créer plusieurs sous-fichiers .c et leurs headers associés. Je dois également compiler selon la compilation séparée et pour cela, j'ai besoin d'écrire un fichier Makefile que voici:
Lorsque j’exécute la commande "make all" et donc que j'essaie de compiler le projet à la fin, j'obtiens énormément de définitions multiples, alors que bien évidémment, je ne les définis qu'une seule fois dans les fichiers:
gcc -g -c fonction.c -o ./objects/fonction.o -Wall -ansi -pedantic -std='c99' -lm
gcc -g -c fonction_str.c -o ./objects/fonction_str.o -Wall -ansi -pedantic -std='c99' -lm
gcc -g -c registre.c -o ./objects/registre.o -Wall -ansi -pedantic -std='c99' -lm
registre.c: In function ‘remplir_struc_registre’:
registre.c:13:2: warning: implicit declaration of function ‘myStrcpy’ [-Wimplicit-function-declaration]
13 | myStrcpy(tab_registre[j].nom, "ZERO");
| ^~~~~~~~
registre.c: In function ‘estUnRegistre’:
registre.c:384:12: warning: implicit declaration of function ‘comparerChaine’ [-Wimplicit-function-declaration]
384 | if(comparerChaine(tab_registre[i].nom, operande)){
| ^~~~~~~~~~~~~~
gcc -g -c main.c -o ./objects/main.o -Wall -ansi -pedantic -std='c99' -lm
gcc ./objects/*.o -o emul-mips -Wall -pedantic -lm
/usr/bin/ld : ./objects/fonction_str.o:/home/dr_steiner/Documents/GitHub/MIPS-project/fonction.h:18 : définitions multiples de « tab_instruction »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/fonction.h:18 : défini pour la première fois ici
/usr/bin/ld : ./objects/fonction_str.o:/home/ydr_steiner/Documents/GitHub/MIPS-project/fonction.h:30 : définitions multiples de « tab_liste_instructions »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/fonction.h:30 : défini pour la première fois ici
/usr/bin/ld : ./objects/fonction_str.o:/home/dr_steiner/Documents/GitHub/MIPS-project/fonction.h:42 : définitions multiples de « tab_liste_instructions_val »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/fonction.h:42 : défini pour la première fois ici
/usr/bin/ld : ./objects/main.o:/home/dr_steiner/Documents/GitHub/MIPS-project/fonction.h:18 : définitions multiples de « tab_instruction »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/fonction.h:18 : défini pour la première fois ici
/usr/bin/ld : ./objects/main.o:/home/dr_steiner/Documents/GitHub/MIPS-project/fonction.h:30 : définitions multiples de « tab_liste_instructions »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/fonction.h:30 : défini pour la première fois ici
/usr/bin/ld : ./objects/main.o:/home/dr_steiner/Documents/GitHub/MIPS-project/fonction.h:42 : définitions multiples de « tab_liste_instructions_val »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/fonction.h:42 : défini pour la première fois ici
/usr/bin/ld : ./objects/main.o:/home/dr_steiner/Documents/GitHub/MIPS-project/registre.h:15 : définitions multiples de « tab_registre »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/registre.h:15 : défini pour la première fois ici
/usr/bin/ld : ./objects/registre.o:/home/dr_steiner/Documents/GitHub/MIPS-project/registre.h:15 : définitions multiples de « tab_registre »; ./objects/fonction.o:/home/yanis/Documents/GitHub/MIPS-project/registre.h:15 : défini pour la première fois ici
collect2: error: ld returned 1 exit status
make: *** [Makefile:10 : all] Erreur 1
Il ne s'agit que de définitions multiples de structures que je définis et initialise dans mes headers, et que je réutilise dans mes fichiers .c. Je pense que la compilation fonctionne une fois lors de la chaque compilation de chaque fichier séparé, mais lorsqu'il essaye de les rassembler, tout plante...
Voici un exemple de mon fichier registre.h qui contient la définition et l'initialisation de la structure "tab_registre":
#ifndef __REGISTRE_H__
#define __REGISTRE_H__
#define NB_REGISTRES 34
typedef struct registre registre;
struct registre {
char *nom; /* NOM du registre */
int id; /* Identifiant associé au registre */
char tab_bin[33]; /* Valeur du registre */
int reg_protege; /* Indique si le registre est protégé en écriture: 1 si protégé, 0 sinon */
};
struct registre tab_registre[NB_REGISTRES + 1];
void remplir_struc_registre(); /* Remplit la structure contenant les différentes informations sur les registres */
void verifier_structure_registre(); /* Affiche les informations actuelles des registres (nom, valeur, protégé ou non) */
int estUnRegistre(char* operande); /* Retourne -1 si l'operande n'est pas un registre. Retourne la valeur correspondante du registre si c'en est un */
int estUnRegistreProtege(char* operande); /* Retourne 1 si l'operande est un registre protege. Retourne 0 sinon */
#endif
Voici également mon fichier registre.c qui utilise dans ses fonctions la structure "tab_registre":
tu le lances depuis quel répertoire ? parce que tu risque de te retrouver avec des objets un peu partout sans savoir réellement lesquels sont utilisés pour l'édition des liens !
Tu as des déclarations implicites également !
Tu utilises des variables globales … qui ne doivent jamais être définies dans un header …
Un fichier d'entête ça ne doit contenir que des déclarations, seul les types peuvent y être définis. On ne doit pas mettre de définition d'instances ou de fonctions dans un entête.
Par exemple dans registre.h, la ligne 15 définit une variable, c'est interdit. Tous les fichiers qui incluent cet entête vous vouloir créer ce tableau tab_registre[], il y a alors plusieurs fois ce tableau, ça n'a pas de sens.
Il ne s'agit que de définitions multiples de structures que je définis et initialise dans mes headers, et que je réutilise dans mes fichiers .c.
Je pense que la compilation fonctionne une fois lors de la chaque compilation de chaque fichier séparé, mais lorsqu'il essaye de les rassembler, tout plante...
Je ne comprends pas pourquoi la compilation plante.
Bonsoir,
Ce n'est pas la compilation qui échoue, mais l'édition des liens. (Cela dit, il y a quelques sévères avertissements lors de la compilation de certains fichiers.)
Pour compléter les précédentes (et excellentes) réponses, ce qui t'a échappé, c'est que lors de chaque compilation d'un fichier *.c, tous les entêtes inclus sont également compilés. Donc il suffit qu'un entête inclus par plusieurs *.c définisse quoi que ce soit, pour se retrouver avec des définitions multiples.
Les fichiers d'en-têtes ne doivent servir qu'à déclarer, jamais à définir (revois bien ces deux notions si elles ne sont pas claires).
- Edité par Marc Mongenet 3 janvier 2022 à 22:03:09
J'ai donc corrigé le problème en définissant les structures dans mes .h, et donc en les initialisant dans le main. Lorsqu'une fonction en a besoin, j'envoie en paramètre un pointeur vers la structure. Comme ceci dans mon main:
typedef struct instructions {
char *instr; /* NOM de l'instruction */
int nb_arg; /* Nombre d'arguments */
int* pos_arg; /* tableau qui indique le numero des bits de debut et de fin des arguments */
char tab_bin[33]; /* Tableau en binaire associé */
int *reg; /* Tableau de la taille du nombre d'arguments. Le tab est à 1 si c'est un registre, 0 si c'est un immediate*/
int *reg_protege; /* Tableau des arguments où il est possible d'écrire dans un registre: 1 si il est possible d'écrire dans un registre, 0 sinon */
}instructions;
Il y a deux exécutables à fabriquer, bin/main qui demande un nombre et affiche son carré, et bin/tests qui fait des tests unitaires (genre vérifier que 4 est le carré de 2). La fonction carre() est déclarée dans src/carre.h et définie dans src/carre.c.
1. La première chose à raconter dans le Makefile, c'est qu'on obtient bin/main :
par édition des liens de 2 fichiers objets qui devraient se trouver dans obj/. Pour faire l'édition des liens, on emploie la macro LINK.c qui s'occupe de récupérer les options $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) ...
Il faut la même chose pour bin/tests
2. Les fichiers objets sont fabriqués à partir des sources qui sont dans src/
5. Reste la question des dépendances. Par exemple, obj/main.o dépend du source src/main.c (c'est implicite) mais aussi de src/carre.h
Il faudrait décrire toutes ces dépendances dans le Makefile
obj/main.o: src/carre.h
obj/tests.o: src/carre.h
C'est fastidieux à écrire, et surtout à maintenir sur la durée du projet.
Heureusement, le compilateur fait ça très bien pour nous si on lui demande gentiment avec l'option -MMD (make makefile dependencies).
CFLAGS += -MMD
Quand le compilateur traite, par exemple, main.c, il fabrique dans obj, à côté de main.o, un fichier main.d qui contient une liste de dépendances explicites :
obj/main.o: src/main.c src/carre.h
Dans le Makefile, il suffit de dire qu'on les inclut
-include $(wildcard obj/*.d)
999. A la fin, ça nous fait quelque chose comme ça
- Edité par michelbillaud 3 janvier 2022 à 10:18:25
Problème de "multiples definitons"
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
En recherche d'emploi.