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 :

define ou const


Sujet :

C

  1. #1
    Membre habitué
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Points : 151
    Points
    151
    Par défaut define ou const
    Bonsoir ,

    Je suis en train d'apprendre la compilation ou programmation modulaire.

    Je ne sais pas répondre à cette question :

    dans le main() , j'ai des chemin de fichier que j'ai codé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char nom_fichier[]="/monchemin/fichier";
    Je sais que je peux utiliser le préprocesseur pour écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    define NOM_FICHIER monchemin/fichier
    mais je peux aussi écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const char nom_fichier = "monchemin/fichier"
    Quels objectifs doivent guider mes choix ?

  2. #2
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 440
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 440
    Points : 43 082
    Points
    43 082
    Par défaut
    Si tu utilises #define, lors de la compilation, chaque occurrence de NOM_FICHIER est remplacé par monchemin/fichier. Il ne s'agit donc pas d'une variable. Cela est fait par le pre-processeur, avant la compilation proprement dite.

    le #define est adapté si ton fichier n'est pas modifié par du code (selon critère x ou y) dans ton source
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  3. #3
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Salut
    Citation Envoyé par frederic13870 Voir le message
    mais je peux aussi écrire : const char nom_fichier = "monchemin/fichier"
    Tu as oublié l'étoile => const char* nom_fichier = "monchemin/fichier".

    En écrivant char nom_fichier[]="toto" tu définis un tableau variable de 5 caractères (5 car il ne faut pas oublier le '\0'). Ce tableau étant situé dans l'espace mémoire de ton code (on l'appelle "la pile") cela signifie que tu pourras modifier ton tableau comme ça te chante (écrire par exemple nom_fichier[0]='x'). La seule obligation est de ne pas dépasser les limites (de nom_fichier[0] à nom_fichier[4]).

    En écrivant char *nom_fichier="toto", tu commences par créer une chaine "toto" située dans l'espace statique de ton code (statique donc non modifiable) et tu récupères juste l'adresse de cette chaine, adresse que tu stockes dans un pointeur nommé "nom_fichier".
    Tu pourras utiliser "nom_fichier" exactement comme un tableau (aller lire par exemple nom_fichier[2]) sauf en ce qui concerne la modification de son contenu qui te sera interdite (contenu situé dans l'espace statique).

    En écrivant define NOM_FICHIER "monchemin/fichier" là tu donnes une directive pré-processeur. Le pré-processeur est une phase de compilation préalable qui transforme un source ".c" en source ".i" (interprété) dans lequel tous les #include ont été réellement inclus, et dans lequel tous les MON_FICHIER que tu peux utiliser sont remplacés par leur valeur. Et c'est ce ".i" qui est lui réellement compilé au final (je crois que l'option "-P" de gcc lui demande de s'arrêter au ".i", à vérifier).
    A l'usage général c'est exactement pareil (que tu exécutes printf("%s\n", mon_fichier) ou bien printf("%s\n", NOM_FICHIER) au résultat c'est pareil). Le code généré lui pourra être un peu plus gros (chaque "NOM_FICHIER" sera littéralement remplacé par la string qui lui est définie) mais aujourd'hui cela n'a plus d'importance. Mais tu ne pourras pas aller lire NOM_FICHIER[2] car "NOM_FICHIER" ce n'est pas un tableau.

    Citation Envoyé par frederic13870 Voir le message
    Quels objectifs doivent guider mes choix ?
    Tes chemins peuvent-ils changer durant l'exécution de ton code ou ne fais-tu que les lire ? Et quand tu les lis, as-tu besoin de n'en lire qu'une partie ? Est-ce que ça te gêne d'avoir la string répétée 500 fois das ton code parce que le préprocesseur la remplace partout où tu utilises NOM_FICHIER ?
    Personnellement je pense que le const pour une variable c'est un peu idiot car le but d'une variable est de varier durant la vie du code. En revanche j'en use beaucoup dans mes paramètres de fonction (parce que je veux m'auto-protéger contre une modif accidentelle).
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    En écrivant char *nom_fichier="toto", tu commences par créer une chaine "toto" située dans l'espace statique de ton code (statique donc non modifiable) et tu récupères juste l'adresse de cette chaine, adresse que tu stockes dans un pointeur nommé "nom_fichier".
    Tu pourras utiliser "nom_fichier" exactement comme un tableau (aller lire par exemple nom_fichier[2]) sauf en ce qui concerne la modification de son contenu qui te sera interdite (contenu situé dans l'espace statique).
    Pour être même très complet :
    - tu pourras dans le code t'en servir comme si c'était modifiable (car le pointeur dit pointer vers des caractères non constants)
    - a l'exécution, le programme va normalement crasher car l'OS va détecter que tu écris à un endroit interdit.
    C'est un bug très vicieux.

  5. #5
    Membre éclairé
    Homme Profil pro
    Architecte de système d'information
    Inscrit en
    Septembre 2015
    Messages
    204
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Architecte de système d'information

    Informations forums :
    Inscription : Septembre 2015
    Messages : 204
    Points : 839
    Points
    839
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Le code généré lui pourra être un peu plus gros (chaque "NOM_FICHIER" sera littéralement remplacé par la string qui lui est définie)
    en fait non

    le compilateur va chercher tous les strings codés en dur dans le code et va les stocker dans une zone à part
    et à chaque appel dans le code, il va appeler cette zone

    tu peux le voir avec un décompilateur, ou avec un éditeur hexa (tu ne trouveras qu'un seul "NOM_FICHIER")

    tu as le même fonctionnement avec les autres types de variables (mais seulement pour celles définies comme variables globales), mais tu ne le vois qu'avec le décompilateur

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    A l'usage général c'est exactement pareil
    en théorie, pas vraiment
    Avec 1 variable const, il devrait y avoir des contrôles de type en + (ce qui semble logique).
    Mais j'ai fait quelques tests sommaires, et même avec des macros, il y a des contrôles. Donc, peut-être dans des cas très précis, comme celui de @Bktero.

    Ensuite, pour reprendre @chrtophe, avec 1 variable const, tu as 1 variable en mémoire ... à moins que les compilateurs modernes optimisent avec des substitutions ou autres.


    Citation Envoyé par Xelland Voir le message
    le compilateur va chercher tous les strings codés en dur dans le code et va les stocker dans une zone à part
    segment RODATA (<- lien wiki en français)

  7. #7
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 440
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 440
    Points : 43 082
    Points
    43 082
    Par défaut
    Envoyé par Sve@r Le code généré lui pourra être un peu plus gros (chaque "NOM_FICHIER" sera littéralement remplacé par la string qui lui est définie)
    en fait non
    En fait si.

    Je suis pas spécialiste en C mais si je compile ceci :
    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
     
    #include <stdio.h>
    #include <stdlib.h>
     
    #define NOM_FICHIER "/etc/fstab"
     
    int main()
    {
        FILE *handle=fopen(NOM_FICHIER,"r");
        char *buffer;
        buffer=malloc(500);
        buffer=fgets(buffer,200,handle);
        printf("%s\n",buffer);
        fclose(handle);
    }
    et que j'ouvre le fichier a.out avec hexedit, il n'y a nulle part NOM_FICHIER, et une occurrence de /etc/fstab.
    NOM_FICHIER est bien remplacé par /etc/fstab
    idem si je compile avec gcc -E essai.c >essai.i pas de trace de NOM_FICHIER.

    edit :

    Par contre, si je remplace par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        const char nom_fichier[]="/etc/fstab";
        FILE *handle=fopen(nom_fichier,"r");
        char *buffer;
        buffer=malloc(500);
        buffer=fgets(buffer,200,handle);
        printf("%s\n",buffer);
        fclose(handle);
    }
    Ca marche mais je ne vois pas /etc/fstab avec hexedit, la chaine doit être encodée.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  8. #8
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Citation Envoyé par chrtophe Voir le message
    Je suis pas spécialiste en C mais si je compile ceci :
    ....
    Ca marche mais je ne vois pas /etc/fstab avec hexedit, la chaine doit être encodée.
    Une compilation ne peut pas être une preuve, d'ailleurs tu n'indiques pas si tu as compilé en optimisé. Les codes sont effectivement différents, le second utilise une variable intermédiaire supplémentaire, mais après optimisation il ne reste aucune différence fonctionnelle donc ça pourrait générer exactement le même code.

    Si on utilise le #define dans plusieurs unités de compilation, au moment de l'édition des liens il y plus de boulot pour voir que c'est la même chose (il va y avoir deux zones constantes qui ont le même contenu mais pas le même nom avec le define et une unique variable dans l'autre cas.)
    Si on fait confiance à l'optimiseur les deux possibilités sont très difficiles à séparer.

  9. #9
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par chrtophe Voir le message
    et que j'ouvre le fichier a.out avec hexedit, il n'y a nulle part NOM_FICHIER, et une occurrence de /etc/fstab.
    NOM_FICHIER est bien remplacé par /etc/fstab
    idem si je compile avec gcc -E essai.c >essai.i pas de trace de NOM_FICHIER.
    C'est normal, le préprocesseur a déjà remplacé la macro par son contenu. Et l'interprété étant issu du préprocesseur (à propos merci pour l'option qui est "-E" et non comme je l'avais écrit "-P") tu ne peux bien évidemment plus voir "NOM_FICHIER".

    En fait, ce que je voulais dire, c'est qu'avec un code qui utilise plusieurs fois la macro "NOM_FICHIER", l'interprété écrira alors plusieurs fois son contenu dans le source généré.
    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>
     
    #define NOM_FICHIER "/etc/fstab"
     
    int main() {
    	printf("%s, %s, %s, %s\n", NOM_FICHIER, NOM_FICHIER, NOM_FICHIER, NOM_FICHIER);
    }

    L'interprété donne
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ... (tout le contenu de stdio.h)...
     
    int main() {
    	printf("%s, %s, %s, %s\n", "/etc/fstab", "/etc/fstab", "/etc/fstab", "/etc/fstab");
    }
    Après j'en ai rapidement supposé qu'écrire 4 fois une string devait donner fatalement un code plus gros qu'écrire 4 fois une variable et effectivement, après test avec un autre code contenant const char *nom_fichier="/etc/fstab" et en utilisant "nom_fichier" à la place de "NOM_FICHIER", les deux codes ont la même taille. Certainement gràce à la technique RODATA exposée par Xelland et complétée par foetus

    Il y a toutefois un autre avantage à passer par une macro: le fait que la macro sera connue de tout le code source (et éventuellement de tous les autres sources si la macro se trouve dans un header inclu par tout le monde), donc toutes les fonctions pourront traiter "NOM_FICHIER" ; chose qui n'est pas possible avec une variable sauf à vendre son âme à la facilité de la globalisation des variables.

    Citation Envoyé par foetus Voir le message
    en théorie, pas vraiment
    Avec 1 variable const, il devrait y avoir des contrôles de type en + (ce qui semble logique).
    J'avais dit "en général" donc plus ou moins sous-entendu "dans les grandes lignes". Dans l'esprit d'un printf("%s\n", NOM_FICHIER) comparé à un printf("%s\n", nom_fichier). J'ai d'ailleurs ensuite donné un exemple plus pointu d'un truc qui fonctionne avec une variable mais pas avec une macro

    Citation Envoyé par foetus Voir le message
    Mais j'ai fait quelques tests sommaires, et même avec des macros, il y a des contrôles.
    Le contrôle se fait à la compilation donc après la phase préprocesseur. S'il y a un contrôle c'est par exemple dans le cadre d'un void fct(int n) à laquelle tu passerais la macro NOM_FICHIER et dans ce cas, le compilateur réagit exactement comme si tu lui passais la string "/etc/fstab" (c'est d'ailleurs en réalité exactement ce qui se passe). Idem avec un truc style int n=NOM_FICHIER+1 qui sera compilé comme int n="/etc/fstab"+1 et donc traité de la même façon.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  10. #10
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 440
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 440
    Points : 43 082
    Points
    43 082
    Par défaut
    C'est normal, le préprocesseur a déjà remplacé la macro par son contenu
    Oui c'est ce que je voulais dire par rapport à la réponse de Xelland, @dalfab, je ne parlais pas de l'aspect taille. Compilé simplement via gcc essai.c sans aucune optimisation, même pas précisé de nom de fichier final. J'ai juste remarqué que ma chaine /etc/fstab n'apparaissait pas en clair, encodé ou compressé, je ne sais pas, je m'attendais à la voir en clair dans le fichier ELF, mais ne pas la voir dans le contexte, ne me dérange pas.

    à propos merci pour l'option qui est "-E" et non comme je l'avais écrit "-P"
    Mais de rien, après c'est pas une option qu'on utilise beaucoup, pour ne pas dire jamais.

    Après j'en ai rapidement supposé qu'écrire 4 fois une string devait donner fatalement un code plus gros qu'écrire 4 fois une variable
    Au niveau assembleur,et donc au niveau exécutable, il stockera 2 chaines : "/etc/fstab", et "%s, %s, %s, %s\n". visible avec la génération du fichier source assembleur avec gcc -S fichier.c, on pourra voir qu'elles sont logiquement stockées dans la section .rodata, comme déjà évoqué.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

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

Discussions similaires

  1. POO, constantes différence Define et Const
    Par SuperArbre dans le forum Langage
    Réponses: 4
    Dernier message: 14/05/2012, 15h05
  2. static const, namespace ou #define ?
    Par Awakening dans le forum Langage
    Réponses: 10
    Dernier message: 21/10/2011, 11h02
  3. problème avec const char * et #define macro
    Par CodeurNé dans le forum C
    Réponses: 5
    Dernier message: 20/09/2006, 21h25
  4. Différences entre #define et const
    Par Tittom dans le forum C
    Réponses: 19
    Dernier message: 01/06/2006, 13h48
  5. [const] #define vs const
    Par Mokhtar BEN MESSAOUD dans le forum C
    Réponses: 34
    Dernier message: 22/12/2005, 17h46

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