Partage
  • Partager sur Facebook
  • Partager sur Twitter

Impossible de créer une structure

J'ai besoin de vous !

    11 mars 2023 à 19:47:54

    Bonjour, je suis en train de suivre le cours de C, donc je suis désolé si le sujet a été résolu. J'ai voulu créer une structure de la manière suivante :

    typedef struct utilisateur {
        int value1;
        int value2;
    };

    et je crée une variable de type "utilisateur"

    #include <stdio.h>
    #include "test.h"
    
    int main() {
        struct utilisateur personne = {0, 0};
    }

    Ce qui renvoie :

    error: variable 'personne' has initializer but incomplete type

    Et je ne comprends pas DU TOUT. Donc si une âme charitable pourrait m'aider à comprendre où est mon erreur... (même à plusieurs dessus on a du mal)

    • Partager sur Facebook
    • Partager sur Twitter

    Internet c'est quand même une belle invention :o

      11 mars 2023 à 20:23:57

      Hello,

      Tu définis une structure qui s'appelle "struct utilisateur", mais tu ne mets pas de nom pour le typedef.

      Tu pourrais écrire

      typedef struct utilisateur {
          .... ;
          .... ;
      } t_utlisateur;
      
      int main() {
          t_utilisateur personne={0};
      }

      ou encore

      struct utilisateur {
          .... ;
          .... ;
      };
      
      int main() {
          struct utilisateur personne={0};
      }

      -
      Edité par edgarjacobs 11 mars 2023 à 20:24:40

      • Partager sur Facebook
      • Partager sur Twitter

      On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

        12 mars 2023 à 15:19:57

        > J'ai voulu créer une structure de la manière suivante :

        Au lieu de tartiner du "créer" à toutes les sauces, il faudrait revenir à un vocabulaire technique plus précis.  On ne crée pas du code, on écrit du code, et ce code  déclare / définit des variables / des types / des fonctions etc. si le compilateur le veut bien.

        Si on daigne regarder ce que nous dit le compilateur quand on lui donne

        typedef struct utilisateur {
            int value1;
            int value2;
        };  
        

        on voit le message

        a.c:4:1: attention: spécificateur de classe de stockage inutile dans la déclaration vide
        

        Google qui est notre ami nous dit que les spécificateurs de classe de stockage, c'est auto (obsolète),register
        static,extern,typedef

        Et visiblement il s'agit ici de typedef.  Mais pourquoi est-il inutile ?

        Parce que typedef, c'est un mot-clé qui se transforme une déclaration de variable en déclaration de type.

        Prenons par exemple ce code

        struct paire {
           int x;
           int y;
        };
        

        qui est une définition du type struct paire.

        On peut utiliser ce type pour déclarer une variable

        struct paire p;

         On aurait pu faire directement

        struct paire {
            int x;
            int y;
        } p;

        Qui combine définition d'un type struct paire, et déclaration d'une variable de ce type.

        Ce nonobstant, la déclaration est de la forme   Type nom_variable;

        Si on met typedef devant

        typedef struct paire  t_paire;

        ça fait une déclaration d'un type qui est équivalent à "struct paire".

        ___

        On aurait pu écrire directement

        typedef struct paire {
            int x;
            int y;
        } t_paire;

        (combinaison d'une déclaration de type  struct paire et d'un type équivalent t_paire)

        ou

        typedef struct {
            int x;
            int y;
        } t_paire;

        (combinaison d'une déclaration de type struct anonyme et d'un type équivalent t_paire)

        Mais ce que tu as écrit

        typedef struct utilisateur {   // ERREUR
            int value1;
            int value2;
        };

        n''est pas correct : il manque le nom dont typedef est censé faire un type.


        ---

        Pour simplifier l'explication, je me suis limité aux typedefs "simple" qui déclarent un type équivalent, maintenant en pratique ça sert à des choses plus complexes, par exemple

        typedef  t_paire(* op_binaire)(t_paire, t_paire);
        

        Ca déclare un type "pointeur sur fonction qui prend deux paires et en renvoie une troisième".





        -
        Edité par michelbillaud 12 mars 2023 à 15:58:52

        • Partager sur Facebook
        • Partager sur Twitter
          13 mars 2023 à 12:19:33

          Une grande confusion est créée par le fait que lorsque les struct sont enseignées, elles le sont en même temps que les typedef, alors que les deux n'ont rien à voir et que l'usage de typedef n'est absolument pas indispensable à la définition des types struct, ni à la déclaration ou usage des variables qui utilisent ces types, et que l'on introduit inutilement des éléments de complexité dans la compréhension de quelque chose qui devrait être très simple.

          struct point {
             double x;
             double y;
          };

          Définit un type "struct point", où "point" est l'étiquette du type "struct point".

          Pour utiliser ce type "struct point" et déclarer des variables utilisant ce type, comme avec n'importe quel type en C, on écrit le nom du type suivit du nom de la variable.

          struct point point_A;

          C'est tout.

          On devrait se contenter lorsqu'on apprend les types struct, de les définir et déclarer sans typedef et de la manière la plus simple possible.

          Une fois que le type struct est bien compris, on peut éventuellement apprendre à utiliser les typedef (que j'utilise quant à moi qu'avec parcimonie) et complexifier en rassemblant en une seule ligne définition de type struct avec ou sans étiquette, alias de type avec typedef et déclaration de variable dans la foulée si on a envie. Mais pour un débutant c'est vraiment trop d'un coup et confus à souhait.

          Une autre difficulté de compréhension est de considérer que typedef sert à définir un type (ce n'est pas vraiment le cas, en dépit du sens apparent du mot clef).

          Cette instruction ne sert qu'à créer un alias d'un autre type, c'est à dire un synonyme d'un autre type C (qui peut être un type struct ou autre d'ailleurs).

          -
          Edité par Dlks 13 mars 2023 à 12:41:13

          • Partager sur Facebook
          • Partager sur Twitter
            13 mars 2023 à 13:26:11

            Dlks a écrit:

            On devrait se contenter lorsqu'on apprend les types struct, de les définir et déclarer sans typedef et de la manière la plus simple possible.

            Très bon conseil pour les gens qui, comme moi, ont du mal avec le C ! Ph. Drix, dans son livre, dit que 'struct', c'est comme 'monsieur' : on ne dit pas Drix mais Monsieur Drix, et c'est pareil avec les structures. J'aime bien. Et j'utilise rarement les 'typedef' (je ne suis pas assez intelligent pour  comprendre leur intérêt).

            • Partager sur Facebook
            • Partager sur Twitter
              13 mars 2023 à 13:47:23

              L'utilisation de typedef ou non est souvent sujet à débat, il y a des pour et des contre mais c'est principalement une question de style.

              Pour ma part je préfère sans car quand je vois une fonction comme :

              void
              f(Foo foo, Bar bar);

              Je n'ai aucune idée de savoir si foo est une une structure, une énumération ou pire, un typedef cachant un pointeur sur une structure. Avant C23, la redéclaration des typedef était interdite ce qui rendait compliqué de faire des déclarations anticipées, d'où la réticence de certains d'utiliser des typedefs.

              Le style varie aussi souvent selon le domaine d'application. Les moteurs de jeux auront quasiment toujours des typedefs car les noms à rallonge seraient fastidieux à écrire. Par contre les développement comme Linux ou OpenBSD ont quasiment jamais de typedef.

              En revanche, les pointeurs de fonctions font parti des très raisons de les utiliser parce que je doute que beaucoup de personne arrivent à lire une signature comme:

                   void
                   (*signal(int sigcatch, void (*func)(int sigraised)))(int);

              -
              Edité par markand 13 mars 2023 à 13:47:48

              • Partager sur Facebook
              • Partager sur Twitter

              git is great because Linus did it, mercurial is better because he didn't.

                13 mars 2023 à 15:53:25

                > En revanche, les pointeurs de fonctions font parti des très [bonnes] raisons de les utiliser

                J'étais tombé par hasard sur un cas où ça avait l'air techniquement indispensable : les fonctions qui retournent un pointeur de fonction

                Exemple, on a une fonction foo

                void foo(void) {
                	printf("Foo !\n");
                }
                

                et on voudrait que la fonction get_action me retourne un pointeur vers cette fonction, pour que je puisse faire

                int main() {
                	get_action()();
                }
                

                (bon ok, sans paramètres ça n'a pas l'air très utile, mais je simplifie)


                Ma solution

                typedef  void (*action)(void);
                
                action get_action(void) {
                	return &foo;
                }

                Mais je ne suis pas arrivé à indiquer le type directement dans le prototype, que ce soit

                void (*)(void)  get_action2(void) {
                	return &foo;
                }
                
                // ou 
                
                (void (*)(void)) get_action3(void) {
                	return &foo;
                }
                


                Le compilateur proteste :

                cc     typdef.c   -o typdef
                typdef.c:15:8: erreur: expected identifier or « ( » before « ) » token
                   15 | void (*)(void) get_action2(void) {
                      |        ^
                typdef.c:19:2: erreur: expected identifier or « ( » before « void »
                   19 | (void (*)(void)) get_action3(void) {
                      |  ^~~~
                
                


                ----

                update, si, on y arrive :

                void (*get_action4(void))(void) {
                	return &foo;
                }

                (obtenu en transposant l'exemple de https://www.geeksforgeeks.org/returning-a-function-pointer-from-a-function-in-c-cpp/ )

                mais je ne comprends pas vraiment. Je modifie pour avoir des actions avec paramètres

                void foo(int n) {
                	printf("Foo %d!\n", n);
                }

                J'appelle comme ça

                int main() {
                	get_action4()(33);
                }
                

                et la déclaration de get_action4, c'est

                void (*get_action4(void))(int n) {
                	return &foo;
                }

                Dont je comprends, après coup, que   ça se lit:

                get_action4 appelée sans paramètres retourne un pointeur vers une fonction void xxxx (int n).

                Ah !;.... Ce n'est pas vraiment intuitif, parce que d'habitude le type de retour est indiqué à gauche du nom de la fonction, et les paramètres à droite.

                Mais bon, c'est la logique (calamiteuse) des déclarations en C, logique qu'on n'a pas l'habitude de retrouver dans des définitions de fonctions.


                Donc, c'est pas techniquement indispensable, mais on va mettre des typedefs !








                -
                Edité par michelbillaud 13 mars 2023 à 16:13:05

                • Partager sur Facebook
                • Partager sur Twitter
                  13 mars 2023 à 16:07:45

                  Yes, parce que la syntaxe du diable est :

                  void (*get_action())()

                  D'ailleurs, cdecl, est un bon outil pour ce genre de cas de figure.

                  • Partager sur Facebook
                  • Partager sur Twitter

                  git is great because Linus did it, mercurial is better because he didn't.

                    13 mars 2023 à 19:51:46

                    Je suis assez d'accord avec tout ce qu'a dit markand et les réflexions de Michel sont toujours enrichissantes.

                    Il y a, à mon sens, un cas où le typedef est "indispensable", c'est lorsque l'on construit une bibliothèque et que l'on souhaite que l'utilisateur de la bibliothèque ne dispose que d'un type opaque.

                    Exactement comme lorsque dans la libc on utilise FILE, qui n'est autre qu'un typedef sur une struct, ou le pthread_t définit par POSIX. Le fonctionnement et le contenu de ces struct sont entièrement gérés par l'implémentation interne de la bibliothèque.

                    L'utilisateur n'a pas savoir concrètement quel est le véritable type ou le contenu de cette structure de données qui varie selon l'implémentation et le système (en gros on ne lui donne aucun moyen direct de le savoir et c'est exprès). Il doit se contenter d'utiliser l'API exposée par la bibliothèque, sans autre moyen direct de farfouiller dans l'implémentation.

                    C'est un moyen, en C, de créer des API avec un fort niveau d'abstraction, avec des bibliothèques qui ont le moins d'effets de bord possibles sur le code existant lorsque ce qui est "sous le capot" change, dès lors que l'API exposée ne change pas.

                    -
                    Edité par Dlks 13 mars 2023 à 19:53:44

                    • Partager sur Facebook
                    • Partager sur Twitter
                      13 mars 2023 à 20:02:18

                      Un autre cas de complication est une fonction qui retourne un pointeur sur un tableau.
                      On peut évidemment combiner en retournant un pointeur sur un tableau de pointeurs de fonctions!!
                      double (*(*fct(int))[3])(float) { ... }
                      // plus lisible en écrivant :
                      typedef  double (*Ptr_fct)(float);
                      typedef  Ptr_fct Tab3Fct[3];
                      Tab3Fct*  fct(int) { ... }
                      • Partager sur Facebook
                      • Partager sur Twitter

                      En recherche d'emploi.

                        29 mars 2023 à 20:10:51

                        edgarjacobs a écrit:

                        Hello,

                        Tu définis une structure qui s'appelle "struct utilisateur", mais tu ne mets pas de nom pour le typedef.

                        Tu pourrais écrire

                        typedef struct utilisateur {
                            .... ;
                            .... ;
                        } t_utlisateur;
                        
                        int main() {
                            t_utilisateur personne={0};
                        }

                        ou encore

                        struct utilisateur {
                            .... ;
                            .... ;
                        };
                        
                        int main() {
                            struct utilisateur personne={0};
                        }

                        -
                        Edité par edgarjacobs 11 mars 2023 à 20:24:40


                        Bonjour,

                        oui effectivement le problème venait de là, mais tout les autres conseils m'ont bien aidés. Je suis en train d'apprendre le C et je ne sais pas encore facilement où et quoi chercher. Merci tout le monde et bonne route !

                        • Partager sur Facebook
                        • Partager sur Twitter

                        Internet c'est quand même une belle invention :o

                        Impossible de créer une structure

                        × 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