IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Variables globales pour compilation séparée


Sujet :

C

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    124
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2010
    Messages : 124
    Par défaut Variables globales pour compilation séparée
    Bonjour

    Je développe un code en C, qui comporte déjà plusieurs fichiers source. Afin d'en faire le code le plus pro possible, j'essaie de respecter au maximum les recommandation de codage.

    Ayant plusieurs variables globales, voici mon problème :

    Pour l'instant, je fais la déclaration de définition dans un fichier en-tête Entete.h qui est inlut dans tous mes fichiers **.c.
    Or, j'ai lu sur plusieurs sites que cette pratique est déconseillée car elle mène à des inclusions multiples si le fichier est inclu plusieurs fois (ce qui est le cas dans mon code, ou Entete.h est inclus dans chacun de mes fichiers **.c). mon code est donc de ce type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    #include <stdio.h>
    #include <math.h>
    #include "Entete.h"
     
    /*******************************************************************************
     * MAIN
     ******************************************************************************/
    int main(void)
    {
      void f1( void );
      t=0;
      dt=0.1;
      f1();
      printf("%f",t);
      return 0;
    }
    fichier aux.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #include "Entete.h"
     
    void f1( void )
    {
    t+=dt;
    return;
    }
    fichier Entete.h :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    double t;
    double dt;
    Concrètement, quel est le problème qui peut se poser ? Car ma compilation ne donne aucune erreur et que le code s'éxécute normalement.

    J'ai lu par ailleurs que pour éviter les inclusions multiples, les fichiers en tête devaient être protégés par des macros comme ceci :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #ifndef H_ENTETE_H
    #define H_ENTETE_H
     
    double t;
    double dt;
     
    #endif
    Cela est-il suffisant ? J'ai lu aussi qu'il fallait mieux, pour être sûr de ne faire les déclarations de définitions qu'une seule fois, de faire celles-ci dans un fichier **.c et de mettre dans le header la déclaration de référence (du style "externe double t") qui sera rappellée dans le nombre de fichier nécessaire au travers de l'"include".

    (cela donnerait le code suivant :

    main.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    #include <stdio.h>
    #include <math.h>
    #include "Entete.h"
     
     int t;
     int dt;
     
    /*******************************************************************************
     * MAIN
     ******************************************************************************/
    int main(void)
    {
      void f1( void );
      t=0;
      dt=0.1;
      f1();
      printf("%f",t);
      return 0;
    }
    fichier aux.c :

    [/CODE]

    fichier aux.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #include "Entete.h"
     
    void f1( void )
    {
    t+=dt;
    return;
    }
    fichier entête :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #ifndef H_ENTETE_H
    #define H_ENTETE_H
     
    extern double t;
    extern double dt;
     
    endif
    )

    Quelle est la différence entre ces deux méthodes ? Laquelle est nécessaire ? Les deux ?

    Merci à toutes les bonnes volontés, car je suis malgré tout bien embrouillée

    L.

  2. #2
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 308
    Billets dans le blog
    5
    Par défaut
    Bonjour.

    Déclarer des variables en globale n'est bien entendu pas interdit. Mais bien souvent on peut facilement s'en passer. Et oui c'est source d'erreurs et d'incompréhension du code.

    Après il faut toujours définir des macros dans tes fichiers d'entête pour effectivement éviter plusieurs inclusions du même code.

    Si on prend un exemple simple : #include <stdio.h>;. Cette inclusion peut être faite dans plusieurs fichiers sources d'un même projet. Si ce fichier d'entête ne disposait pas de macro ton compilateur te le ferait raipdement savoir .

  3. #3
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    C'est normal que le compilateur ne te donne aucune erreur. La compilation en tant que tel ne fait que vérifier si le code que tu as rendu est syntaxiquement correct, c'est à dire vérifiera si la "grammaire" du langage est respecté. Dans ton cas, c'est le cas. Tu utilises des variables globales, qui ont été définis ultérieurement, il sait de quoi il en retourne, pas de problème.

    Imagine le cas où tu possèdes plusieurs fichier .c différent, et qu'ils font tous partie d'un grand tout qui est ton exécutable final. Tu inclus ton .h dans les différents .c en ayant besoin, tu les compiles séparément. Pas de soucis, tout est bon.
    Par contre, lors de l'édition des liens, c'est à dire le moment ou tu vas rassembler ensemble tout tes .c pour donner un et un seul code exécutable, PATATRA tout s’effondre. Pourquoi ?

    Le problème avec les variables globales.. C'est que justement, elles sont globales. (captain obvious, ici !)
    Comme tu inclus plusieurs fois le même .h, il y a plusieurs déclaration pour une variable ayant le même nom. Le linker va crier au scandale car il existera alors plusieurs variable possédant le même nom, ce qui est interdit.


    La première manière d'éviter cela, c'est d'effectivement passer par des #define.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #ifndef H_ENTETE_H
    #define H_ENTETE_H
    Ceci se résume en gros à dire "Si le define H_ENTETE_H existe déjà, alors je ne rentre pas dans ce qui va suivre". Quand tu inclura pour la première fois ton .h, le define sera créé et par extension tout autre .h portant le marquage ci-dessus sera exclu. Cela permet d'éviter les multiples déclarations pour une même variable.

    En plus de cela, la deuxième méthode que tu exposes est aussi la bonne, mais tu l'utilises mal. L'idée est de faire un .c séparé contenant la déclaration de tes variables globales.
    Le bon usage demande au maximum de ne pas déclarer de variable dans les entêtes (.h). (hormis tout ce qui est de type #define.)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    /* var_globale.c */
    int    test;
    int    antitest;
    Et dans ton .h, tu y fera référence.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    /* var_globale.h */
    extern int test;
    extern int antitest;
    Le mot clé extern permet de spécifier au compilateur qu'il existe quelque part une variable du nom et du type précisé, et qu'il peut donc compiler sans émettre d'erreur. Il ne s'agit pas d'une déclaration de variable !
    Réellement, le compilateur ne sait pas où se trouve la variable, ni ce qu'elle contient. On se contente de dire qu'il peut l'utiliser.
    Plus tard, lors de l'édition des liens, c'est le linker fera le rapprochement entre les extern présent dans le fichier var_globale.h et le fichier var_globale.c et qui permettra ainsi à l'exécutable de fonctionner normalement.


    La première méthodes dont je viens de parler est à utiliser tout le temps. Cela t'évitera bien des désagréments à l'avenir.
    La deuxième méthode, je n'ai pas encore assez d'expérience pour pouvoir l'affirmer, mais il me semble que dans la mesure du possible, on la combine pour la déclaration des variables globale.

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 453
    Par défaut
    Bonjour,

    Citation Envoyé par latitude38 Voir le message
    Bonjour
    Je développe un code en C, qui comporte déjà plusieurs fichiers source. Afin d'en faire le code le plus pro possible, j'essaie de respecter au maximum les recommandation de codage.
    C'est une excellente chose. Cela dit, garde toujours un peu de recul, car certaines pratiques prônées par certains seront violemment décriées par d'autres. Se donner des lignes directrices pour des raisons valables est vertueux, mais je me méfie des gens qui s'attardent trop sur la forme. C'est généralement signe qu'il n'y a pas grand chose derrière.

    Ayant plusieurs variables globales, voici mon problème :

    Pour l'instant, je fais la déclaration de définition…
    La déclaration consiste à annoncer l'existence d'une ressource. La définition consiste à en spécifier la valeur, ou le détail quand il s'agit de types.

    … dans un fichier en-tête Entete.h qui est inlut dans tous mes fichiers **.c.
    Or, j'ai lu sur plusieurs sites que cette pratique est déconseillée car elle mène à des inclusions multiples si le fichier est inclu plusieurs fois (ce qui est le cas dans mon code, ou Entete.h est inclus dans chacun de mes fichiers **.c).
    L'inclusion de *.h dans les différentes unités de compilation est tout-à-fait recommandée. C'est à cela qu'ils servent. Pour éviter les redéfinitions, on procède exactement comme tu l'as fait : en mettant une garde implémentée par un #if en début et fin de fichier.

    En revanche, un *.h ne doit contenir que des déclarations, ou des définitions de types. Mais par nature rien qui ne doit être instancié, ce qui se produit automatiquement quand tu déclares une variable. Ça signifie également qu'un *.h peut contenir des prototypes de fonctions, mais que le corps de celles-ci doivent se trouver dans un *.c, qui lui sera compilé proprement et donnera naissance à un fichier objet. Ça veut dire qu'il est correct d'écrire extern double t; dans un *.h pour indiquer à toutes tes unités qu'une telle variable existe et qu'on en donnera l'adresse définitive à l'édition des liens, mais pas double t; seul, qui va faire naître une instance de la variable concerné dans chaque fichier qui inclura ton *.h, et te conduira à te retrouver avec des doublons au moment d'éditer les liens.

    C'est aussi une des raisons pour lesquelles il faut éviter de recourir aux globales, d'une manière générale, quand c'est possible : http://www.developpez.net/forums/d93...c/#post5271611

    Si tu veux malgré tout utiliser une globale, tu fais effectivement un extern dans ton *.h et tu choisis l'unique fichier *.c dans lequel elle doit exister en pratique.

    Concrètement, quel est le problème qui peut se poser ? Car ma compilation ne donne aucune erreur et que le code s'éxécute normalement.
    Ça ne marche que parce que tu inclus ton fichier qu'une seule fois.

    J'ai lu par ailleurs que pour éviter les inclusions multiples, les fichiers en tête devaient être protégés par des macros comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #ifndef H_ENTETE_H
    #define H_ENTETE_H
     
    double t;
    double dt;
     
    #endif
    En effet.

    Cela est-il suffisant ? J'ai lu aussi qu'il fallait mieux, pour être sûr de ne faire les déclarations de définitions qu'une seule fois, de faire celles-ci dans un fichier **.c et de mettre dans le header la déclaration de référence (du style "externe double t") qui sera rappellée dans le nombre de fichier nécessaire au travers de l'"include".

    (cela donnerait le code suivant :

    […]

    Quelle est la différence entre ces deux méthodes ? Laquelle est nécessaire ? Les deux ?
    La dernière.

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    J'ai écrit un article sur le sujet "regrouper les variables globales dans un fichier" : https://gradot.wordpress.com/2014/07...globales-en-c/ Il fait écho à un article que javais écrit sur le même sujet 2 ans plus tôt. J'espère que ça t'intéressera.

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    124
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2010
    Messages : 124
    Par défaut
    Merci de toutes vos réponses, je suis plus éclairée.

    Reste trois petites questions :
    * Je ne comprend toujours pas pourquoi ma compilation et l'execution de ce code (simplifié pour l'occasion) fonctionne normalement alors qu'il y a des doublons de définition (j'inclus Entete.h dans main.c ET dans aux.c)....Pourquoi ? Le fait que cela fonctionne me donnerait presque envie de ne pas me servir des 2 techniques pour éviter la fameuse 'inclusion multiple' !

    * Ces 2 méthodes ont apparemment le même effet, et l'un de vous me recommande de me servir plutot de la déclaration de référence "extern double t" dans le fichier Entete.h. La macro "#ifndef H_ENTETE_H ..." n'est donc pas nécessaire, vous me confirmez ?

    * Troisième chosse : en lisant la réponse de l'un de vous, je me demande si il est recommandé de regrouper toutes les déclarations de définition dans un seul fichier **.c . Qu'en pensez vous ? (Mis à part 'Bktero' qui m'a déjà donné son avis).

    Merci à vous

  7. #7
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Citation Envoyé par latitude38 Voir le message
    Merci de toutes vos réponses, je suis plus éclairée.

    Reste trois petites questions :
    * Je ne comprend toujours pas pourquoi ma compilation et l'execution de ce code (simplifié pour l'occasion) fonctionne normalement alors qu'il y a des doublons de définition (j'inclus Entete.h dans main.c ET dans aux.c)....Pourquoi ? Le fait que cela fonctionne me donnerait presque envie de ne pas me servir des 2 techniques pour éviter la fameuse 'inclusion multiple' !
    Es-tu sur de bien faire l'édition des liens ? De faire un seul et unique exécutable regroupant tes divers .c ?
    Je viens encore de faire un test chez moi, et j'obtiens bien l'erreur : /tmp/ccJNqEaj.o:(.data+0x0): définitions multiples de « mavar_global ».

    Tu DOIS te servir de ces deux techniques, ce n'est pas négociable si tu souhaites produire quelque chose de "pro" comme tu dis. Sinon, on risque bien de te maudire jusqu'à la 25ème génération !

    * Ces 2 méthodes ont apparemment le même effet, et l'un de vous me recommande de me servir plutot de la déclaration de référence "extern double t" dans le fichier Entete.h. La macro "#ifndef H_ENTETE_H ..." n'est donc pas nécessaire, vous me confirmez ?
    Importante, certainement elle l'est !
    Elles ont toutes deux "le même effet" visible. Mais elles ne font pas les mêmes choses. Les deux sont complémentaire.
    Je laisse le soin à d'autre expliquer le pourquoi du comment, je n'arrive pas à formuler quelque chose de facilement compréhensible.

    * Troisième chosse : en lisant la réponse de l'un de vous, je me demande si il est recommandé de regrouper toutes les déclarations de définition dans un seul fichier **.c . Qu'en pensez vous ? (Mis à part 'Bktero' qui m'a déjà donné son avis).
    Tu as lu l'article de Bktero ?
    Je n'ai rien de plus à ajouter, il expose point par point le pourquoi du comment. Je suis entièrement de son avis.

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    124
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2010
    Messages : 124
    Par défaut
    Oui je crée bien un seul et unique executable, et je compile tous les fichiers ensemble avec gcc (sur codeblocks).
    Bizarre, bizarre... (si quelqu'un a une idée du pourquoi du comment, je suis preneuse..)

    En tout cas, c'est noté, je retiens ces 2 techniques.

  9. #9
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Si ton fichier "entete.h" est toujours semblable à celui montré ci-dessus, c'est normal : il n'y a pas de définition de variables dedans, uniquement des déclarations externes. Tu peux l'inclure autant de fois que tu veux dans autant de fichiers que tu veux.

    Les gardes d'inclusion ne protègent que de l'inclusion du même .h plusieurs fois dans un unique .c ! Elles ne protègent pas de l'inclusion unique mais répétée dans plusieurs fichiers .c.

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    124
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2010
    Messages : 124
    Par défaut
    Non mon fichier .h est le même qu'initiallement c'est à dire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    double t;
    double dt;
    Cette déclaration (et définition) étant incluse dans deux fichiers .c distincts.
    Mon code fonctionne donc, malgré cette maladresse...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Variables globales et compilation séparée.
    Par dré kam dans le forum Débuter
    Réponses: 10
    Dernier message: 12/11/2011, 23h18
  2. [Débutant C#]Variable Globale pour deux forms
    Par progfou dans le forum C#
    Réponses: 7
    Dernier message: 20/02/2007, 09h08
  3. variable globale pour griser des boutons de commande
    Par ben5985 dans le forum Access
    Réponses: 5
    Dernier message: 24/11/2006, 11h34
  4. Variable globales pour mon appli
    Par soufir dans le forum Servlets/JSP
    Réponses: 3
    Dernier message: 07/06/2006, 11h51
  5. variable globale pour plusieurs Form
    Par ced2004 dans le forum Windows Forms
    Réponses: 1
    Dernier message: 05/04/2005, 08h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo