Partage
  • Partager sur Facebook
  • Partager sur Twitter

Transmettre valeur d'une variable

entre plusieurs fonctions, pointeurs ?

    20 juillet 2021 à 15:11:14

    Bonjour à tous,

    Je vais essayer d'être clair et concis dans ma question :

    - J'ai une fonction Main qui appelle une deuxième fonction qui appelle une troisième fonction.

    J'ai besoin d'un paramètre du main dans ma troisième fonction.

    Je me retrouve donc a devoir mettre cette variable dans les paramètres de la deuxième fonction pour la stocker inutilement, puis la troisième. Ce qui augmente mon nbre de param par fonctions et défait la lisibilité du code, et sa simplicité.

    Je suis débutant sur les pointeurs, mais il y aurais moyen de créer un pointeurs dans le main pour simplement retrouver la donné dans la troisième fonction, non ? Ou il y a encore plus efficace histoire de simplifier le code ?

    PS : Je pourrais simplement essayer, mais je sais que ça va me prendre bcp de temps (je galère encore avec les pointeurs, la syntaxe m'est pas naturelle etc..) Si quelqu'un comprend ma question je suivrai la démarche la plus efficace.

    Merci à tous et bonne journée.

    • Partager sur Facebook
    • Partager sur Twitter
      20 juillet 2021 à 15:28:08

      Hello,

      Ce n'est pas un problème d'avoir un paramètre qui ne sert qu'à être donné à une autre fonction, c'est courant si l'on découpe son code en petites fonctions.
      Maintenant si ça te dérange, peut-être que ton code manque un peu de logique ? Peut-etre que main() devrait appeler la 3eme fonction après avoir appelé la seconde ?

      La logique du code tient aussi dans les noms que tu donnes aux fonctions et aux variables ;)

      Tu peux essayer de manipuler des variables globales si tu ne connais pas encore cette notion.

      Les structures permettent de regrouper plusieurs variables en une seule, tu peux regarder ça aussi !

      La logique et les bonnes pratiques viendront avec l'entrainement (et en lisant/posant des questions sur les forums!).

      • Partager sur Facebook
      • Partager sur Twitter
        20 juillet 2021 à 15:35:20

        Ok, ça me rassure. Je pensais forcer la logique avec cette méthode.

        Effectivement j'ai pu un peu apprendre les struct dans le cours du site. Je voulais justement refaire un programme du cours (jeu du plus ou du moins) mieux découpé en plusieurs fonctions et en ajoutant des fonctionnalités (notamment le fait de sauvegarder les score dans un .txt).

        Je vais creuser sur tes conseils, merci !

        Bonne journée à toi.

        PS (edit) : Du coup, concernant ma question initiale. Il serait possible de créer un pointeur dans le main pour la variable que je veux réutiliser dans la 3eme fonction ? Et ainsi pouvoir l'enlever de la fonction 2 (qui la stock) afin de récupérer la valeur dans la 3eme fonction ?

        -
        Edité par BetaDuCentaure 20 juillet 2021 à 16:11:46

        • Partager sur Facebook
        • Partager sur Twitter
          20 juillet 2021 à 16:42:26

          PaulMulin a écrit:

          PS (edit) : Du coup, concernant ma question initiale. Il serait possible de créer un pointeur dans le main pour la variable que je veux réutiliser dans la 3eme fonction ? Et ainsi pouvoir l'enlever de la fonction 2 (qui la stock) afin de récupérer la valeur dans la 3eme fonction ?

          Pour qu'une fonction accède à une donnée voulue on peut :
          - Lui filer la donnée en paramètre
          - Ou alors la fonction en appelle une autre qui retourne la donnée voulue
          - Ou alors la donnée est dans une variable globale

          Les variables globales c'est trop facile à utiliser, du coup c'est facile aussi de faire du code qui perd en logique.
          Est-ce que c'est mieux de filer la donnée en paramètre, ou bien laisser la fonction en appeler une autre pour récupérer la donnée en question ? Je ne sais pas, ça dépend des contextes ...

          • Partager sur Facebook
          • Partager sur Twitter
            20 juillet 2021 à 16:43:02

            Et comment la troisième fonction connaitra-t-elle le pointeur défini dans le main()? Il faudra bien le passer en paramètre à un moment.

            Il n'y a rien de miraculeux dans les pointeurs, ce sont des variables comme les autres.

            -
            Edité par edgarjacobs 20 juillet 2021 à 16:50:57

            • Partager sur Facebook
            • Partager sur Twitter

            Il y a ceux qui font des sauvegardes, et ceux qui n'ont pas encore eu d'incident....

              20 juillet 2021 à 16:53:47

              Gam' => Ok, merci pour la précision supplémentaire.

              Edgarjacobs => Oui, vu comme ça c'est vrai que mon idée pourrait paraitre inutile. Elle pourrait peut-être permettre de sauter une déclaration de paramètres dans la fonction numéro deux, au mieux..

              Je vais rester sur cette méthode et creuser les variables globales; ça me revient je l'ai également vu dans le cours du site. ça pourrait clarifier un peu mon code.

              Bonne journée à vous.

              • Partager sur Facebook
              • Partager sur Twitter
                21 juillet 2021 à 20:29:56

                Bonjour,

                Attention, une variable globale n'est jamais une clarification, ça donne plutôt quelque chose d'obscur (on récupère quelque chose par "magie"). On ne devrait jamais envisager d'utiliser une globale.

                Ta troisième fonction travaille pour ta deuxième fonction, c'est à cette dernière de lui transmettre les consignes. Si une des consignes vient de main(), elle doit servir de relai. Si ton code ne te satisfait pas, peut-être trouveras-tu plus tard avec les structures d'autres possibilités. Mais te ne devrais pas utiliser de globales, il faut surtout apprendre à s'en passer le plus possible.

                Tu peux aussi nous présenter l'exemple de ce que tu cherches à faire pour une réponse plus détaillée.

                • Partager sur Facebook
                • Partager sur Twitter

                En recherche d'emploi.

                  22 juillet 2021 à 10:15:25

                  J'utilise des variables globales dans deux contextes, et à chaque fois ça m'évite de les passer sans cesse en paramètres de fonction, donc je crois que ça ressemble aux préoccupations de PaulMulin. Mais je ne suis pas un pro du C (je l'utilise pour les loisirs), donc si j'en parle, c'est aussi pour qu'on me dise en quoi c'est une mauvaise idée (je n'en suis pas convaincu...)

                  1) Le but du programme est de créer un fichier graphique (en postscript, donc c'est un bête fichier texte) à partir de diverses données et de nombreux calculs. J'ai mis en variable globale celle qui représente le fichier de sortie (de type FILE*), sciemment pour éviter de la traîner en paramètres des fonctions. Au début je ne le faisais pas et j'oubliais souvent de la mettre en paramètre, d'où perte de temps. J'ai cédé : variable globale ! De toute façon c'est une donnée qui ne varie jamais dans le programme, donc les pièges que j'ai lus à propos des variables globales (difficulté à contrôler leurs modifications) sont hors-sujet. Là maintenant, je trouve que c'est défendable.

                  2) Les paramètres du programme (options de couleur, couleurs, orientation, dimensions, tailles des marges, dispositions des dessins...) sont nombreux (de l'ordre de la centaine). J'aurais pu les organiser en structures et sous-structures, mais c'est pénible d'écrire sans cesse truc.machin.chose, j'ai donc fait plusieurs structures (pour n'écrire que machin.chose), et du coup ça me faisait cinq ou six structures à passer en paramètres (et encore : cinq ou six pointeurs sur ces structures, et les pointeurs et moi, bof...) (Ce serait à refaire, j'en ferais des variables simples. Tiens, c'est pas idiot...) Donc les paramètres du programme sont en variables globales. Le seul inconvénient que je vois, c'est si un jour je veux utiliser les fonctions qui dépendent des paramètres dans un autre programme : il faudra ré-écrire ces variables. Mais ça va vite de faire un chercher/remplacer...

                  (Remarque : dans mes programmes, les noms des variables globales commencent toujours par une majuscule, pas les autres variables, donc je m'y retrouve.)

                  Tiens, j'ai une question : dans les commandes Unix, est-ce que les options sont mises dans des variables globales ? (Je trouverais ça logique.) Si je retrouve un site qui fournit leurs sources, j'y jetterais peut-être un œil...

                  -
                  Edité par robun 22 juillet 2021 à 10:19:20

                  • Partager sur Facebook
                  • Partager sur Twitter
                    22 juillet 2021 à 11:47:44

                    bah il y a toujours la possibilité d'encapsuler les accès à une variable globale dans des fonctions, de rendre ladite variable static dans un module pour en interdire l'accès par les autres modules de manière directe. Mais ce n'est qu'un moyen un peu bâtard de contourner les problèmes des variables globales dans un gros projet sans pour autant en éviter tous les inconvénients, le majeur étant l'impossibilité de tester correctement le programme. C'est une sorte de succédané qui ressemble à un singleton et qui se transforme vite en objet fourre-tout, quasi un demi-dieu.
                    • Partager sur Facebook
                    • Partager sur Twitter
                      22 juillet 2021 à 14:11:19

                      robun a écrit:

                      J'ai mis en variable globale celle qui représente le fichier de sortie (de type FILE*), sciemment pour éviter de la traîner en paramètres des fonctions. 

                      Bien souvent on voit dans des codes des fichiers ouvert qui n'ont pas vraiment lieu d'être constamment ouvert. On peut travailler sur un buffer en mémoire, puis faire la sauvegarde une fois le travail terminé. Traîner un descripteur de fichier, c'est peut-être parce qu'il y a un problème de conception. 

                      • Partager sur Facebook
                      • Partager sur Twitter
                        22 juillet 2021 à 14:49:22

                        robun a écrit:

                        [...]
                        Tiens, j'ai une question : dans les commandes Unix, est-ce que les options sont mises dans des variables globales ? (Je trouverais ça logique.) Si je retrouve un site qui fournit leurs sources, j'y jetterais peut-être un œil...

                        -
                        Edité par robun il y a environ 4 heures


                        Si on regarde l'implémentation GNU des coreutils (ls, echo, rm, chmod, …) on voit que … ça dépend. Certains utilisent des variables globales (ls, echo) d'autre non (rm) par exemple.

                        Très personnellement, j'aurai tendance, pour des projets autre que de petits exemples, à ne pas utiliser de variables globales, y compris pour les options parsées. Une structure décrivant un état initialisée par un parser d'option que l'on donne aux fonctions qui en ont besoin (et elles ne seront pas nombreuses) me semble plus simple.

                        Le dernier recours étant une sorte de singleton accessible de tout endroit du programme uniquement via des fonctions et non directement, cela permet d'en tracer l'accès a minima. Idéalement il ne pourra être initialisé qu'automatiquement et sera immutable.

                        • Partager sur Facebook
                        • Partager sur Twitter
                          22 juillet 2021 à 16:36:51

                          Zero.c a écrit:

                          On peut travailler sur un buffer en mémoire, puis faire la sauvegarde une fois le travail terminé.

                          J'aime bien cette idée !

                          Mais c'est le buffer qui risque de devenir une variable globale...

                          White Crow a écrit:

                          Si on regarde l'implémentation GNU des coreutils (ls, echo, rm, chmod, …) on voit que … ça dépend. Certains utilisent des variables globales (ls, echo) d'autre non (rm) par exemple.

                          Merci d'avoir jeté un oeil !

                          White Crow a écrit:

                          Une structure décrivant un état initialisée par un parser d'option que l'on donne aux fonctions qui en ont besoin (et elles ne seront pas nombreuses) me semble plus simple.

                          Ah, c'est peut-être là où j'ai un problème de conception : je ne devrais pas avoir besoin des paramètres dans de nombreuses fonctions.

                          -
                          Edité par robun 22 juillet 2021 à 16:44:00

                          • Partager sur Facebook
                          • Partager sur Twitter
                            23 juillet 2021 à 19:29:36

                            Salut,

                            Perso, pour des variables auxquelle j'accède souvent, je les planque en static dans une fonction. D'abord parce que ce ne sont pas vraiment des variables. Il s'agît le plus souvent de l'environnement de mon programme (la structure d'inputs, l'affichage (SDL_Renderer*)) et d'autres infos.

                            Par exemple pour la SDL2 / mixer :

                            /** \brief Video display status
                             */
                            typedef struct CEV_VideoDisplay
                            {
                                int screenW, /**< Display pixel width */
                                    screenH, /**< Display pixel height */
                                    logicW,  /**< Render logic width */
                                    logicH;  /**< Render logic height */
                            
                                float proportion; /**< Display ratio as W/H */
                            
                                char *type;       /**< Screen type as string */
                            }
                            CEV_VideoDisplay;
                            
                            
                            /** \brief Sound system
                             */
                            typedef struct CEV_SoundSystem
                            {
                                unsigned char musicVolume,  /**< Music volume applied */
                                              sfxVolume;    /**< Sfx volume applied */
                            
                                unsigned int channelNum;    /**< num of channel opened */
                            
                                CEV_Music *loadedMusic;     /**< Actually playing music */
                            }
                            CEV_SoundSystem;
                            
                            
                            
                            /** \brief Video system
                             */
                            typedef struct CEV_VideoSystem
                            {
                                bool isFullScreen;          /**< fullscreen on/off */
                            
                                SDL_Window      *window;    /**< Main window */
                                SDL_Renderer    *render;    /**< Main renderer */
                                CEV_VideoDisplay   info;    /**< Display infos */
                            
                            }
                            CEV_VideoSystem;
                            
                            
                            /** \brief Global system
                            */
                            typedef struct CEV_MainSystem
                            {
                                CEV_VideoSystem   video; /**< Main video sytem */
                                CEV_SoundSystem   sound; /**< Main sound system */
                                CEV_Input         input; /**< Main input struct */
                            }
                            CEV_MainSystem;
                            
                            //** les fonctions de récup : **/
                            
                            CEV_MainSystem* L_systemSet(CEV_MainSystem* sys)
                            {/*sets mainSystem pointer to be fetched**/
                            
                                static CEV_MainSystem *system = NULL;
                            
                                if(sys != NULL)
                                    system = sys;
                            
                                return system;
                            }
                            
                            
                            CEV_MainSystem* CEV_systemGet(void)
                            {/*fetches main system pointer**/
                            
                                return L_systemSet(NULL);
                            }
                            
                            
                            CEV_SoundSystem* CEV_soundSystemGet(void)
                            {/*fetches sound system pointer**/
                            
                                return &(CEV_systemGet()->sound);
                            }
                            
                            
                            CEV_VideoSystem* CEV_videoSystemGet(void)
                            {/*fetches video system pointer**/
                            
                                return &(CEV_systemGet()->video);
                            }
                            
                            
                            CEV_Music *CEV_playingMusicGet(void)
                            {
                                return CEV_soundSystemGet()->loadedMusic;
                            }
                            

                            Tous les Set sont fait à l'initialisation du système puis quand j'ai besoin de récupérer un trucr dans une fonction je passe :

                            SDL_Renderer *render = CEV_videoSystemGet()->render;
                            //ou
                            CEV_Input *input = CEV_inputGet();

                            et ça m'évite de les trimballer partout en argument. Et comme c'est ma lib perso "standard" j'évite d'avoir à réfléchir si je dois mettre des pointeurs sur ces éléments dans mes structures "application".

                            ça semble con d'avoir des fonctions qui se contentent de renvoyer la partie du retour d'une autre fonction, mais ça permet des raccourcis avec des termes clairs.

                            Ce que je fait parfois aussi pour éviter de trimballer trop de truc, c'est avoir des pointeurs sur les autres structures utiles. Par exemple dans mon jeu, la structure d'environnement possède un pointeur vers le joueur pour réagir à celui-ci (faire bouger l'herbe quand le joueur passe par exemple). Inversement, la structure de mon joueur possède un pointeur vers l'environnement pour la mise à jour de son statut (collisions, etc..).

                            Bref, ce ne sont pas les solutions qui manquent. Je me suis fixé une limite de 4 arguments, au-delà, j'envisage autre chose en me disant que j'ai probablement mal pensé un truc. Bien entendu, je fais des exceptions quand je n'ai pas le choix, si je crée un menu avec 7 items, ça me fera 8 arguments :

                            static CEV_BarMenu* L_BCKGRD_menu(void)
                            {//main bar menu config
                            
                                CEV_BarMenu* result = CEV_barMenuCreate(3, "File", "Settings", "Help");
                            
                                if(IS_NULL(result))
                                {
                                    fprintf(stderr, "Err at %s / %d : bar menu creation failed.\n", __FUNCTION__, __LINE__ );
                                    return NULL;
                                }
                            
                                CEV_barMenuAddLabelContent(result, "File", 7, "New background", "Load background", "Save", "Import..", "Delete", "Return", "Quit");
                                CEV_barMenuAddLabelContent(result, "Settings", 7, "Config..", "Select Picture", "Add layer", "Remove layer", "Swap Layers", "Set Camera", "Set World");
                                CEV_barMenuAddLabelContent(result, "Help", 2, "About...", "Tips");
                                CEV_barMenuFontSet(result, mainFont);
                            
                                CEV_barMenuAutoAdjust(result);
                            
                                return result;
                            }

                            Et enfin, ici mainFont (ligne 15) est une variable globale au projet, parce que pourquoi pas, même si j'évite généralement. Ma seule variable globale "standard" est mon compteur d'erreur de lecture/ecriture dans les fichiers, par exemple :

                            char readWriteErr = 0;/**< global r/w survey */
                            
                            uint32_t read_u32le(FILE* f)
                            {/*lecture u32 bits dans fichier le*/
                                uint8_t b[4];
                            
                                if(fread(b, 1, sizeof(b), f) != sizeof(b))
                                    readWriteErr++;
                            
                                return 0U | b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
                            }
                            
                            void write_u32le(uint32_t val, FILE* f)
                            {/*ecriture en le dans fichier*/
                                uint8_t b[4];
                            
                                b[0] = val       & 0xff;
                                b[1] = (val>>8)  & 0xff;
                                b[2] = (val>>16) & 0xff;
                                b[3] = (val>>24) & 0xff;
                            
                                if(fwrite(b, 1, sizeof(b), f) != sizeof(b))
                                    readWriteErr++;
                            }
                            
                            

                            C'est mon petit errno à moi dédié aux fichiers.

                            Bonne continuation.

                            -
                            Edité par drx 23 juillet 2021 à 19:40:03

                            • Partager sur Facebook
                            • Partager sur Twitter

                            Stringman devient Bonhomme !! | Jeux de plateforme : Nouvelle Démo. (màj : 24/04/2021)

                              23 juillet 2021 à 21:41:34

                              En fait il y a deux soucis, à des niveaux différents.

                              • En soi, il n'y a pas de mal à avoir des données globales, si elles doivent être accessibles d'un peu tous les coins d'un programme. Elles font partie d'un "contexte d'exécution", comme la table des descripteurs ouverts, les signaux, etc. C'est juste que le contexte est partagé implicitement.

                              Cas de figure, un fichier journal pour noter des trucs au fur et à mesure d'un programme. Un log, quoi. Le plus simple c'est d'avoir une variable statique 

                              FILE * log;
                              int log_level; // un niveau, pendant qu'on y est
                              
                              int main(int argc, char **argv) {
                                 log_level = quelque chose qui dépend des paramètres;
                                 log = fopen("....", "w");
                                 ...
                              }
                              

                               mais bon, passer ça en paramètre à toutes les fonctions, merci de la bonne idée...

                              • Quand on enseigne la programmation aux débutants (les 3 premiers mois, disons), on est obligé de beaucoup insister  pour qu'ils utilisent le passage de paramètres (*), alors on est un peu obligé d'être lourdement dogmatique sur "pas de variables globales". Sinon, ils déclarent la variable i en global et s'en servent pour toutes les boucles for, en pensant qu'ils sont des génies de l'optimisation...

                              Parce que, comme les projets de programmation qu'on leur donne à faire sont petits, ils les feraient tout aussi bien en collant tout dans le main sans faire aucune fonction (exemple ici, le jeu de plus ou moins). C'est un des obstacles classiques à l'apprentissage : si on voit comment résoudre un problème avec ce qu'on connaît déjà, on traîne des pieds pour apprendre un moyen de faire autrement - qui semblera automatiquement demander plus de travail (ça s'applique à la conception orientée objets, au style fonctionnel, à la récursivité, etc).

                               

                              (*) si on suit la progression habituelle (**) : variables, lire/écrire, expressions, alternative, boucles et ensuite un jour lointain fonctions. Ah oui, on est un peu coincé en C, vu que pour découper une action en appels de fonctions, va vite falloir des pointeurs. Alors on traîne pour (ne pas) en causer.

                              (**) qui est celle inaugurée (1958) par le manuel de FORTRAN II (la version précédente n'avait pas de fonctions), comme quoi il y a une tradition pour aborder le sujet un jour plus tard.

                              https://archive.computerhistory.org/resources/text/Fortran/102653989.05.01.acc.pdf

                              -
                              Edité par michelbillaud 23 juillet 2021 à 22:26:55

                              • Partager sur Facebook
                              • Partager sur Twitter

                              Transmettre valeur d'une variable

                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                              • Editeur
                              • Markdown