Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème de "multiples definitons"

Sujet résolu
    29 décembre 2021 à 18:38:19

    Bonjour à tous,

    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:

    BIN = emul-mips
    OBJECTS = fonction.o fonction_str.o registre.o main.o
    OBJECTS_LOCATION = ./objects/
    OBJECTS_O = ./objects/*.o
    CC = gcc
    RM = rm -f
    CFLAGS = -Wall -ansi -pedantic -std='c99' -lm
    
    all : $(OBJECTS)
    	$(CC) $(OBJECTS_O) -o $(BIN) -Wall -pedantic -lm
    
    fonction_str.o : fonction_str.c fonction_str.h
    	$(CC) -g -c fonction_str.c -o $(OBJECTS_LOCATION)fonction_str.o $(CFLAGS)
    
    fonction.o : fonction.c fonction.h
    	$(CC) -g -c fonction.c -o $(OBJECTS_LOCATION)fonction.o $(CFLAGS)
    
    registre.o : registre.c registre.h
    	$(CC) -g -c registre.c -o $(OBJECTS_LOCATION)registre.o $(CFLAGS)
    
    
    main.o : main.c fonction.h fonction_str.h registre.h
    	$(CC) -g -c main.c -o $(OBJECTS_LOCATION)main.o $(CFLAGS)
    
    clean :
    	$(RM) $(OBJECTS) $(BIN)
    



    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":

    #include "registre.h"
    
    #include <stdlib.h>
    #include <stdio.h>
    
    
    
    void verifier_structure_registre(){
    	int i = 0, j = 0;
    	printf("----------------------------------------------------------\n");
    	for(i = 0; i < NB_REGISTRES + 1; i++){
    		printf("Registre: %s\n", tab_registre[i].nom);
    		printf("Valeur actuelle du registre: ");
    		for(j = 0; j < 33; j++){
    			printf("%d", tab_registre[i].tab_bin[j]);
    		}
    		printf("\nRegistre protege ? ");
    		if(tab_registre[i].reg_protege == 1){
    			printf("OUI\n");
    		}
    		else{
    			printf("NON\n");
    		}
    		printf("----------------------------------------------------------\n");
    	}
    }
    

    Et le fichier main.c: (au cas où)

    #include "fonction.h"
    #include "fonction_str.h"
    #include "registre.h"
    
    
    
    int main(int argc, char* argv[]){
    
        char *fic_instr = argv[1]; /* ./in/instr.txt */
        char *fic_result = argv[2]; /* ./out/result.txt */
        lireInstruction(fic_instr, fic_result);
        verifier_structure_instruction(fic_instr);
        verifier_structure_registre();
        estUnRegistreProtege("S7");
        return 0;
    }
    

    Je ne comprends pas pourquoi la compilation plante.

    Merci d'avance et bonne soirée.






    • Partager sur Facebook
    • Partager sur Twitter
      29 décembre 2021 à 18:49:45

      Houlalala … le makefile est mal foutu !

      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 …

      • Partager sur Facebook
      • Partager sur Twitter
        29 décembre 2021 à 21:35:00

        Bonjour,

        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.

        • Partager sur Facebook
        • Partager sur Twitter

        En recherche d'emploi.

          1 janvier 2022 à 3:29:05

          MaxEnergyzz a écrit:

          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

          • Partager sur Facebook
          • Partager sur Twitter
            2 janvier 2022 à 19:35:45

            Merci à vous tous, c'était bien ça ! :D

            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:

            #include "../headers/fonction.h"
            #include "../headers/fonction_str.h"
            #include "../headers/registre.h"
            
            
            int main(int argc, char* argv[]){
                char *fic_instr = argv[1]; /* ./in/instr.txt */
                char *fic_result = argv[2]; /* ./out/result.txt */
            
                int nb_instructions_entree = compte_nb_inst(fic_instr);
                struct registre tab_registre[NB_REGISTRES + 1];
                struct instructions tab_instruction[NB_INSTRUCTIONS_MIPS];
                struct liste_instructions tab_liste_instructions[nb_instructions_entree]; 
                struct liste_instructions tab_liste_instructions_val[nb_instructions_entree]; 
            
                initialiserEmulateur(fic_instr, fic_result, &tab_registre, &tab_instruction, &tab_liste_instructions);
                verifier_structure_instruction(fic_instr, tab_liste_instructions);
                verifier_structure_registre(tab_registre);
            
                return 0;
            }

            Le code d'une structure:

            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; 



            J'obtiens cependant quelques avertissements:

            #include "../headers/fonction.h"
            #include "../headers/fonction_str.h"
            #include "../headers/registre.h"
            
            
            int main(int argc, char* argv[]){
                char *fic_instr = argv[1]; /* ./in/instr.txt */
                char *fic_result = argv[2]; /* ./out/result.txt */
            
                int nb_instructions_entree = compte_nb_inst(fic_instr);
                struct registre tab_registre[NB_REGISTRES + 1];
                struct instructions tab_instruction[NB_INSTRUCTIONS_MIPS];
                struct liste_instructions tab_liste_instructions[nb_instructions_entree]; 
                struct liste_instructions tab_liste_instructions_val[nb_instructions_entree]; 
            
                initialiserEmulateur(fic_instr, fic_result, &tab_registre, &tab_instruction, &tab_liste_instructions);
                verifier_structure_instruction(fic_instr, tab_liste_instructions);
                verifier_structure_registre(tab_registre);
            
                return 0;
            }


            J'ai l'intime conviction que ce n'est pas totalement ça... Est ce l'un de vous pourrez m'aiguiller sur le sujet SVP ?

            White Crow a écrit:

            Houlalala … le makefile est mal foutu !

             Est ce que tu aurais un exemple sur lequel je pourrais me baser pour avoir un makefile un peu plus propre ?

            Merci d'avance et bonne soirée !



            • Partager sur Facebook
            • Partager sur Twitter
              2 janvier 2022 à 20:36:09

              MaxEnergyzz a écrit:

              J'obtiens cependant quelques avertissements:

              On les voit bien !? 

              • Partager sur Facebook
              • Partager sur Twitter
              ...
                3 janvier 2022 à 10:13:35

                MaxEnergyzz a écrit:

                un exemple sur lequel je pourrais me baser pour avoir un makefile un peu plus propre ?

                Bon, voila la construction "étape par étape" d'un Makefile pour un projet où on veut séparer les répertoires des sources, des objets, des exécutables.

                On utilise gnu-make.

                La structure est la suivante

                .
                ├── bin
                ├── Makefile
                ├── obj
                └── src
                    ├── carre.c
                    ├── carre.h
                    ├── main.c
                    └── tests.c
                
                3 directories, 5 files
                


                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 :

                bin/main: obj/main.o obj/carre.o
                	$(LINK.c) -o $@ $^
                

                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/

                obj/%.o: src/%.c
                	$(COMPILE.c) -o $@ $<
                

                pareil, on utilise une macro qui fait le job.

                3. Bref on en est à ce Makefile

                CFLAGS = -std=c11
                CFLAGS += -Wall -Wextra -Werror
                
                all: bin/main bin/tests
                
                bin/main: obj/main.o obj/carre.o
                	$(LINK.c) -o $@ $^
                
                bin/tests: obj/tests.o obj/carre.o
                	$(LINK.c) -o $@ $^
                
                obj/%.o: src/%.c
                	$(COMPILE.c) -o $@ $<
                
                

                Qui a l'air de marcher :

                $ make
                cc -std=c11 -Wall -Wextra -Werror   -c -o obj/main.o src/main.c
                cc -std=c11 -Wall -Wextra -Werror   -c -o obj/carre.o src/carre.c
                cc -std=c11 -Wall -Wextra -Werror    -o bin/main obj/main.o obj/carre.o
                cc -std=c11 -Wall -Wextra -Werror   -c -o obj/tests.o src/tests.c
                cc -std=c11 -Wall -Wextra -Werror    -o bin/tests obj/tests.o obj/carre.o
                


                A part qu'il ne gère pas les dépendances liées aux #includes

                4. On peut alléger (?) en utilisant des variables et $(addprefix)

                PROGS = main tests
                EXECS  = $(addprefix bin/, $(PROGS))
                
                all: $(EXECS)
                
                bin/main: $(addprefix obj/, main.o carre.o)
                	$(LINK.c) -o $@ $^
                
                ...
                

                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

                # Makefile pour petit projet 
                
                CFLAGS = -std=c11
                CFLAGS += -Wall -Wextra -Werror
                CFLAGS += -MMD
                
                # ---------------------------------------------
                
                PROGS = main tests
                
                MAIN_OBJECTS = main.o carre.o
                TESTS_OBJECTS = tests.o carre.o
                
                all: $(addprefix bin/, $(PROGS))
                
                bin/main: $(addprefix obj/, $(MAIN_OBJECTS))
                	$(LINK.c) -o $@ $^
                
                bin/tests: $(addprefix obj/, $(TESTS_OBJECTS))
                	$(LINK.c) -o $@ $^
                
                
                # ---------------------------------------------
                
                obj/%.o: src/%.c
                	$(COMPILE.c) -o $@ $<
                
                
                -include $(wildcard obj/*.d)
                
                clean:
                	$(RM) *~ */*~ obj/*
                
                mrproper: clean
                	$(RM) bin/*
                
                



                L'arborescence après la compilation (et avant de faire le ménage),

                .
                ├── bin
                │   ├── main
                │   └── tests
                ├── Makefile
                ├── obj
                │   ├── carre.d
                │   ├── carre.o
                │   ├── main.d
                │   ├── main.o
                │   ├── tests.d
                │   └── tests.o
                └── src
                    ├── carre.c
                    ├── carre.h
                    ├── main.c
                    └── tests.c
                
                3 directories, 13 files
                







                -
                Edité par michelbillaud 3 janvier 2022 à 10:18:25

                • Partager sur Facebook
                • Partager sur Twitter

                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.
                • Editeur
                • Markdown