Partage
  • Partager sur Facebook
  • Partager sur Twitter

Erreur de compilation d'une structure "globale"

Erreur de compilation d'une structure "globale"

Sujet résolu
    2 septembre 2021 à 10:03:30

    Bonjour cher lecteur ! :)

    Description du problème : j'ai mon fichier principal, main.c, qui contient ceci :

    #include <stdio.h>
    #include "Etape_1.c"
    
    struct F_l{
        char oct;
        unsigned int eff;
    };
    struct F_l _leaves[256];
    
    int main(void) {
        Blablabla
    }

    Mon deuxième fichier source, Etape_1.c, contient ceci : 

    #include <stdio.h>
    #include "Etape_1.h"
    
    /*
    Fontions qui utilisent la structure F_l et le tableau _leaves
    */

    Et enfin mon fichier header, Etape_1.h, contient ceci :

    #ifndef _ETAPE_1_H
    #define _ETAPE_1_H
    
    /*
    Déclaration des fonctions de Etape_1.c
    */
    
    #endif // _ETAPE_1_H

    Je programme avec Code::Blocks, en console (pour l'instant ^^)

    Mais c'est là que ça coince. Parce que le compilateur me retourne une dizaine de messages d'erreur, me disant que le tableau de structure _leaves n'est pas déclaré dans "Etape_1.c", et qu'il ne connaît pas non plus la structure F_l dans le fichier "Etape_1.c"

    Je sais ce que tu dois te dire : "Mais c'est pas vrai, encore une topic sur un bug de compilation avec une structure globale ? Il y a déjà eu mille sujets résolus du même cas. Ce type ne sait donc pas chercher ?". Et pourtant, j'ai passé trois jours entiers à chercher une solution à mon problème. J'ai trouvé notemment https://openclassrooms.com/forum/sujet/structure-et-define-communs-a-plusieurs-fichiers et https://forums.commentcamarche.net/forum/affich-12251825-meme-structure-dans-plusieurs-fichiers-en-c, mais ce qui a retenu mon attention était ceci : 

    Le plus simple c'est de faire un "commun.h" dans lequel tu mets toutes les définitions, de cette forme :
    #ifndef COMMUN.H
    #define COMMUN.H
     
    #include "commun_t.h" // Pour les structures
    #include "commun_d.h" // Pour les defines
    #include "commun_f.h" // Pour les fonctions
     
    #endif

    ...Et donc, j'ai crée un fichier "commun.h" que j'ai inclus dans "Etape_1.c". Ce "commun.h" contenait ceci :

    typedef struct F_l F_l;
    extern F_l _leaves[];

    Mais le compilateur m'a tout de même retourné quatre erreurs, que je vous redonne ici :

    ||=== Build: Debug in PROJET - Algorithme De Huffman (compiler: GNU GCC Compiler) ===|
    commun.h|2|error: array type has incomplete element type 'F_l' {aka 'struct F_l'}|
    Etape_1.c||In function 'Swap':|
    Etape_1.c|53|error: storage size of 'sav' isn't known|
    Etape_1.c|53|warning: unused variable 'sav' [-Wunused-variable]|
    commun.h|2|error: array type has incomplete element type 'F_l' {aka 'struct F_l'}|
    Etape_1.c||In function 'Swap':|
    Etape_1.c|53|error: storage size of 'sav' isn't known|
    Etape_1.c|53|warning: unused variable 'sav' [-Wunused-variable]|
    ||=== Build failed: 4 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|
    

    Si vous avez besoin du code complet pour comprendre ces erreurs, pas de soucis :)

    Du coup, je pense que c'est le contenu de mon fichier "commun.h" qui cause ces erreurs de compilation. J'ai donc essayé plusieurs combinaisons, sans résultats. Le compilateur me met tour à tour une "redéfinition" de la structure ou du tableau, ou bien un message d'erreur du type "array type has incomplete element type 'F_l' {aka 'struct F_l'}".

    Et donc je suis perdu ...:( Je sais plus du tout quoi faire.

    Merci d'avance pour votre aide ! :)

    • Partager sur Facebook
    • Partager sur Twitter
      2 septembre 2021 à 10:09:35

      Bonjour ! Est-ce que tu es conscient que tu ne fais pas les choses de la façon habituelle ?

      Je ne sais pas si c'est l'explication de l'erreur, mais habituellement on fait :

      /* main.c */
      
      #include <stdio.h>
      #include "Etape_1.h"   // point h, pas point c !
      

      et tu devrais déclarer la structure dans le fichier point h.

      (Quand tu as utilisé commun.h, tu l'avais #inclus dans le main.c ?)

      • Partager sur Facebook
      • Partager sur Twitter
        2 septembre 2021 à 10:26:33

        (Quand tu as utilisé commun.h, tu l'avais #inclus dans le main.c ?)

        Non. Je viens d'essayer avec, et il y a encore plus d'erreurs de compilation

        tu devrais déclarer la structure dans le fichier point h.

        C'est fait. Ce qui me donne :

        #include <stdio.h>
        #include "Etape_1.h"   // .h
         
        struct F_l{
            char oct;
            unsigned int eff;
        };
        struct F_l _leaves[256];
         
        int main(void) {
            Blablabla
        }

        Etape_1.c :

        #include <stdio.h>
        
        /*
        Fonctions qui utilisent la structure F_l et le tableau _leaves
        */

        Etape_1.h :

        #ifndef _ETAPE_1_H
        #define _ETAPE_1_H
        #include "commun.h"
        
        /*
        Déclaration des fonctions de Etape_1.c
        */
        
        #endif // _ETAPE_1_H
        

        Et il y a autant d'erreurs ! :o

        • Partager sur Facebook
        • Partager sur Twitter
          2 septembre 2021 à 10:59:09

          Un peu de rigueur, quand "il y a plein d'erreurs", il FAUT LES LIRE. Et essayer de comprendre ce que ça dit, en particulier le premier message.

          Par exemple

          commun.h|2|error: array type has incomplete element type 'F_l' {aka 'struct F_l'}|

          Ca veut dire que le type "struct f_l", n'est pas connu en compilant commun.h.

          Et si tu veux qu'on t'en dise quelque chose, il faut nous les MONTRER.

          Bon, à l'aveugle :

          • dans "Etape_1.c", tu as des "Fonctions qui utilisent la structure F_l et le tableau _leaves", parait il.
          • mais dans Etape_1.c", tu ne fais aucun #include qui permettrait au compilateur de savoir ce que c'est.
          Tu crois qu'il va deviner par magie ce que tu veux faire ?
          ----
          Le bon ordre pour faire les choses
          • machin.h : fichier d'entete qui contient les définitions de types et les prototypes des fonctions concernant les machins (qui seront définies dans machin.c)
          • machin.c : les définitions de fonctions concernant les machins. Contient un #include "machin.h"
          • prog.c: un programme qui utilise des machins.  Contient un #include "machin.h"
          ---
          Si il y a une variable globale,
          • la déclarer comme "extern" dans machin.h
          • la définir dans machin.c
          Exemple
          ::::::::::::::
          point.h
          ::::::::::::::
          #ifndef POINT_H
          #define POINT_H
          
          struct point {
          	int x, y;
          };
          
          extern struct point points[10];
          
          void decaler(struct point *p, int dx, int dy);
          
          #endif
          
          
          :::::::::::::
          point.c
          ::::::::::::::
          #include "point.h"
          
          
          struct point points[10] = { {0, 0} };
          
          void decaler(struct point *p, int dx, int dy) {
          	p->x += dx;
          	p->y += dy;
          }
          
          ::::::::::::::
          prog.c
          ::::::::::::::
          #include <stdio.h>
          #include "point.h"
          
          int main() {
          	decaler (& points[3], 11, 22);
          	printf("(%d, %d)\n", points[3].x, points[3].y);
          }
          

          -
          Edité par michelbillaud 2 septembre 2021 à 11:18:26

          • Partager sur Facebook
          • Partager sur Twitter
            2 septembre 2021 à 11:09:09

            Tu dois déclarer la structure dans le fichier .h puisque le fichier .c va l'utiliser.

            Et effectivement il faut #inclure le fichier .h dans le fichier .c

            • Partager sur Facebook
            • Partager sur Twitter
              2 septembre 2021 à 11:11:18

              Pour les erreurs, voici ce que j'ai pu trouver :

              Dans Etape_1.c :

              '_leaves' undeclared

              Et dans commun.h, ligne 2 (extern F_l _leaves[];) :

              array type has incomplete element type 'F_l' {aka 'struct F_l'}|

              Et j'ai cherché, mais je comprends pas l'origine de l'erreur ni comment la solutionner


              mais dans "Etape_1.c", tu ne fais aucun #include qui permettrait au compilateur de savoir ce que c'est.

              Donc, il faudrait que que je #include "commun.h" et "Etape_1.h" dans "Etape_1.c" ? Mais j'ai déjà #include "commun.h" dans "Etape_1.h"

              -
              Edité par Darkjura 2 septembre 2021 à 11:13:58

              • Partager sur Facebook
              • Partager sur Twitter
                2 septembre 2021 à 11:21:11

                Darkjura a écrit:

                Et j'ai cherché, mais je comprends pas l'origine de l'erreur ni comment la solutionner


                Donc, il faudrait que que je #include "commun.h" et "Etape_1.h" dans "Etape_1.c" ? Mais j'ai déjà #include "commun.h" dans "Etape_1.h"


                1. C'est pas comme si on s'était embêté à te le dire.

                2. "j'ai déja inclus".  Le compilateur ne va pas te mettre des points pour ta bonne volonté si tu n'as pas inclus le bon truc.

                3. Fais un dessin

                • qui inclut qui (des ronds pour les noms de fichiers, des flèches pour les inclusions)
                • dans quel fichier se trouvent les déclarations.

                -
                Edité par michelbillaud 2 septembre 2021 à 11:23:33

                • Partager sur Facebook
                • Partager sur Twitter
                  2 septembre 2021 à 12:25:47

                  Avec le dessin, j'ai abouti à ceci :

                  • "main.c" doit #inclure "Etape_1.h"
                  • "Etape_1.c" doit #inclure "Etape_1.h"
                  • Et enfin, chaque fichier (.c et .h) doit #inclure "commun.h" car chaque fichier (.c et .h) utilise la structure

                  J'ai juste ? o_O

                  EDIT :

                  Super ! Ca marche ! par contre, j'ai dû enlever "commun.h", car apparemment je n'avais pas juste …

                  En fait j'avais pas vu ton gros edit, michelbillaud, désolé. Donc "main.c" #inclus "Etape_1.h" qui contient ceci :

                  #ifndef _ETAPE_1_H
                  #define _ETAPE_1_H
                  
                  struct F_l {
                      unsigned char oct;
                      unsigned int eff;
                  };
                  extern struct F_l _leaves[256];
                  
                  // Fonctions
                  
                  #endif // _ETAPE_1_H
                  

                  Et "Etape_1.c" #inclus "Etape_1.h". "Etape_1.c" contient ceci :

                  #include <stdio.h>
                  #include "Etape_1.h"
                  
                  struct F_l _leaves[256] = { {0 , 0} };
                  
                  // Fonctions

                  Par contre j'ai une question : Pourquoi y a-t-il besoin d'inclure <stdio.h> dans les deux .c ?

                  -
                  Edité par Darkjura 2 septembre 2021 à 12:46:41

                  • Partager sur Facebook
                  • Partager sur Twitter
                    2 septembre 2021 à 13:57:19

                    Darkjura a écrit:


                    Par contre j'ai une question : Pourquoi y a-t-il besoin d'inclure <stdio.h> dans les deux .c ?

                    Enlève-le d'un des fichiers et tu verras

                    1. si tu en avais vraiment besoin dans ce fichier

                    2. ce que ça raconte quand on en a vraiment besoin, et qu'on a oublié  de le mettre.

                    C'est important de faire ce type de petites expériences par soi-même.

                    Parce qu'oublier un include, ça arrive tout le temps. En faisant l'expérience, on voit quel genre de symptômes ça produit.  Et ensuite, quand on a le même genre de symptôme, ça nous donne un suspect plausible.

                    • Partager sur Facebook
                    • Partager sur Twitter
                      2 septembre 2021 à 15:41:33

                      Je te conseille de ne pas utiliser de commun.h, je trouve que ça complique encore plus.
                      • Partager sur Facebook
                      • Partager sur Twitter
                        2 septembre 2021 à 17:02:56

                        C'est un tout petit peu hors sujet, quoi que …

                        Il y a quelques bons principes à appliquer lors du design de ton programme. Il y a par exemple celui qui recommande de programmer en ayant une interface en tête et non une implémentation. Ici interface ne signifie pas un GUI, une interface graphique, mais une interface qui expose une structure de donnée dont on ne voit pas les détails accompagnée des fonctions de base permettant de la manipuler.

                        En C moderne, cela signifie que tu dois, pour chaque structure de données, fournir un header qui permette d'instancier une structure de données et de la manipuler sans que l'utilisateur sache quoi que soit de tes choix d'implémentations. On peut prendre l'exemple d'une pile d'entiers. On peut proposer une interface comme :

                        #pragma once
                        
                        struct istack;
                        
                        struct istack *istack_new(void);
                        void istack_free(struct istack *);
                        
                        bool istack_is_empty(struct istack *);
                        
                        bool istack_push(struct istack *, int);
                        bool istack_pop(struct istack *);
                        
                        int istack_top(struct istack *);
                        

                        Quelqu'un qui doit utiliser une pile d'entiers n'a pas besoin de plus pour s'en tirer. Il arrive à créer et détruire une pile, vérifier qu'elle est ou non vide, empiler et dépiler des éléments et accéder à la valeur en tête de pile. Rq : j'ai utilise le pragma once en lieu et place d'un header guard … mais c'est anecdotique.
                        Voilà le contenu d'un header istack.h qui expose une interface. Dès que tu as besoin d'une pile d'entiers quelque part dans ton programme tu n'as qu'à inclure ce header et hop !

                        Il faut ensuite aussi en donner une implémentation. Là tu auras un header d'implémentation qui va exposer les détails, comme par exemple ce header que l'on pourrait appeler istack-impl.h :

                        #pragma once
                        
                        struct istack_node {
                            int value;
                            struct istack_node *next;
                        }
                        
                        struct istack {
                            size_t count;
                            struct istack_node *head;
                        }
                        
                        struct istack_node istack_node_new( int, struct istack_node *);
                        void istack_node_free(struct istack_node *);
                        
                        .......

                        et un source qui implémente tout ça :

                        #include "istack.h"
                        #include "istack=impl.h"
                        
                        ....


                        Cela a sans doute l'air de compliquer les choses, mais cela apporte de nombreux avantages au nombre desquels on peut citer :

                        • une indépendance entre la manière d'implémenter une pile et l'utilisation que tu en fais, c'est un couplage faible. tu peux du jour au lendemain décider d'implémenter une version plus efficace ou moins gourmande ou avec plus de fonctionnalités de la pile sans avoir à modifier tout ton code ;
                        • produire une implémentation facilement testable ;
                        • avoir un découpage en «modules» qui seront plus facilement réutilisables ou factorisable en bibliothèque ;
                        • tu vois directement ce que le code fait pas comment il le fait ⇒ meilleure lisibilité ;
                        • ...

                        C'est ce qu'on appelle, dans le cas de  bibliothèques, une API (application programming interface). C'est très utile et très commun, et cela évite des bugs tout en améliorant la maintenabilité et la lisibilité ⇒ que du bonheur.

                        edit: typos dans le code

                        -
                        Edité par White Crow 2 septembre 2021 à 17:26:02

                        • Partager sur Facebook
                        • Partager sur Twitter
                          3 septembre 2021 à 10:57:15

                          @White Crow : Merci pour toutes ces précisions, retenu ! ;)

                          • Partager sur Facebook
                          • Partager sur Twitter
                            11 septembre 2021 à 14:43:55

                            Je reviens sur ce topic parce que, je m'en doutais, mais si j'ai besoin d'un second fichier qui utilise ce tableau de structure, la compilation ne se fait pas

                            main.c :

                            #include "Etape_1.h"
                            #include "Etape_2.h"

                            Etape_2.h :

                            #ifndef _ETAPE_2_H
                            #define _ETAPE_2_H
                            
                            extern struct F_l _leaves[256];
                            
                            void Huffman(void);
                            
                            #endif // _ETAPE_2_H
                            

                            Etape_2.c :

                            #include <stdio.h>
                            #include "Etape_2.h"
                            
                            void fonction(void) {
                                // utilise la structure et le tableau _leaves
                            }
                            


                            Je n'ai pas modifié les fichiers Etape_1.c et Etape_1.h

                            Après avoir essayé plusieurs choses, je viens ici car je ne sais vraiment plus quoi faire

                            Edit :

                            Une seule erreur de compilation : on m'indique que le compilateur ne connaît pas de structure "F_l" dans "Etape_2.h" quand je déclare le tableau de structure en "extern"

                            -
                            Edité par Darkjura 11 septembre 2021 à 14:47:19

                            • Partager sur Facebook
                            • Partager sur Twitter
                              11 septembre 2021 à 15:23:36

                              Tu as changé des choses entretemps.

                              Montre nous le contenu des fichiers.

                              Dans etape2.h, il faut inclure le fichier .h qui a la déclaration de struct f_l.

                              Ps: donne des noms PARLANTS et simples aux types et aux fichiers. Machin1 et machin2, c'est insupportable, il n'y a aucun moyen de se rappeler qui contient quoi.

                              -
                              Edité par michelbillaud 12 septembre 2021 à 15:48:25

                              • Partager sur Facebook
                              • Partager sur Twitter
                                11 septembre 2021 à 15:42:41

                                Dans etape2.h, il faut inclure le fichier .h qui a la déclaration de struct f_l.

                                Incroyable ! :lol:

                                C'était cela le problème ! Ca marche ! :D 

                                Merci michelbillaud ! ;) Je bascule en sujet résolu :)

                                • Partager sur Facebook
                                • Partager sur Twitter

                                Erreur de compilation d'une structure "globale"

                                × 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