Partage
  • Partager sur Facebook
  • Partager sur Twitter

reference et pointeur difference

    26 août 2023 à 3:30:50

    Bonjour.

    je ne vois pas à quoi sert les pointeurs.

    si je fais ça:


    int maVariable = 6;

    cout << &maVariable ;

    comme ça j'obtiendrai l'adresse memoire de maVariable, et comme ça:

    cout << maVariable ;

    j'obtiendrai la valeur de cette adresse.

    ---------------------------------------------------------

    et si je fais ça:

    int maVariable = 6;

    int *monPointeur(&maVariable );

    cout << monPointeur;

    comme ça j'obtiendrai l'adresse de maVariable, et si j'ecris ça:

    cout << *monPointeur;

    j'obtiendrai la valeur de maVariable.

    donc à quoi sert les pointeurs ? je ne vois pasde difference entre pointeur et reference

    -
    Edité par SamuelAlgeski 26 août 2023 à 13:35:50

    • Partager sur Facebook
    • Partager sur Twitter
      26 août 2023 à 9:57:15

      Si tu ne vois pas à quoi ils servent, et bien c'est que tu n'en as pas eu besoin et c'est bien car en C++ on évite de les utiliser. Il est nettement préférable d'utiliser les références. Quand tu en aura vraiment besoin, tu les remplacera par des std::unique_ptr.
      ()Les pointeurs sont très utilisé en C, car en C les références n'existe pas). 
      PS : Utilises le bouton code </> du forum pour poster ton code ! (tu peux modifier ton post, lien modifier en haut à droite du post).
      • Partager sur Facebook
      • Partager sur Twitter
      ...
        26 août 2023 à 11:05:45

        Bonjour,

        Le message qui suit est une réponse automatique activée par un membre de l'équipe de modération. Les réponses automatiques leur permettent d'éviter d'avoir à répéter de nombreuses fois la même chose, ce qui leur fait gagner du temps et leur permet de s'occuper des sujets qui méritent plus d'attention.
        Nous sommes néanmoins ouverts et si vous avez une question ou une remarque, n'hésitez pas à contacter la personne en question par Message Privé.

        Pour plus d'informations, nous vous invitons à lire les règles générales du forum

        Merci de colorer votre code à l'aide du bouton Code

        Les forums d'Openclassrooms disposent d'une fonctionnalité permettant de colorer et mettre en forme les codes source afin de les rendre plus lisibles et faciles à manipuler par les intervenants. Pour cela, il faut utiliser le bouton Code de l'éditeur, choisir un des langages proposés et coller votre code dans la zone prévue. Si vous utilisez l'éditeur de messages en mode Markdown, il faut utiliser les balises <pre class="brush: cpp;">Votre code ici</pre>.

        Merci de modifier votre message d'origine en fonction.

        Liens conseillés

        • Partager sur Facebook
        • Partager sur Twitter
          26 août 2023 à 12:25:29

          SamuelAlgeski a écrit:

          je ne vois pas à quoi sert les pointeurs.

          donc à quoi sert les pointeurs ?

          Ils ne servent pas à obtenir l'adresse d'un truc.

           > je ne vois pas de difference entre pointeur et reference
          • Un pointeur est une donnée contenant une adresse.
          • Une référence est un nom pour une donnée.

          donnée = une zone de la mémoire contenant une information (codée en binaire etc).

          • Partager sur Facebook
          • Partager sur Twitter
            26 août 2023 à 13:35:15

            rouIoude a écrit:

            Si tu ne vois pas à quoi ils servent, et bien c'est que tu n'en as pas eu besoin et c'est bien car en C++ on évite de les utiliser. Il est nettement préférable d'utiliser les références. Quand tu en aura vraiment besoin, tu les remplacera par des std::unique_ptr.
            ()Les pointeurs sont très utilisé en C, car en C les références n'existe pas). 
            PS : Utilises le bouton code </> du forum pour poster ton code ! (tu peux modifier ton post, lien modifier en haut à droite du post).

            dans ce tutorial en language C, on voit qu'il utilise des references & c'est grace à ce reference qu'on obtient  l'adresse memoire, pourquoi donc passer par pointeur ? la reference nous donnes l'adresse memoire aussi !
            • Partager sur Facebook
            • Partager sur Twitter
              26 août 2023 à 14:43:05

              Les références n'existent pas en langage C, l'esperluète (&) y est utilisée dans 2 contextes:

              - devant une expression, elle en donne l'adresse; que l'on stockera dans un pointeur.

              int*  ptr = &a;  // ptr pointe sur a

              - entre 2 expressions, c'est l'opérateur de "et bits à bits"

              int  x =  7 & 20; // on obtient le résultat du "et bits à bits" donc ici 4

              En C++, l'esperluète est utilisable dans un troisième contexte:
              - derrière un type, elle définit un nom qui va référencer une donnée.

              int&  ref = a;  // ref est une référence sur a.
              • Partager sur Facebook
              • Partager sur Twitter

              En recherche d'emploi.

                26 août 2023 à 14:48:57

                imagineons que tu ais cree une class object qui a un nom

                class Object
                {
                private :
                    std::string m_name;
                
                public :
                    Object() {m_name = "noName";}
                    Object(std::string name) {m_name = name;}
                
                    std::string getName() {return m_name;}
                };

                tu souhaite dans une autre partie de ton code stocker les object que tu cree alors tu pourais faire quelque chose comme ca 

                std::vector<Object> tabObject;
                
                    tabObject.push_back(Object("object1"));
                    tabObject.push_back(Object("object2"));
                    tabObject.push_back(Object("object3"));

                cela fonctionne cependant imagineons que tu souhaite parfois changer l'object 1 alors tu ferras 

                tabObject[0] = Object("object4");

                regardons cette partie, tu cree un Object avec le nom object4 "Object("object4")", que tu copie dans la case [0] du vector tu construit donc 2 fois "Object("object4")" ce qui n'est pas optimiser, il se passe la meme chose dans "tabObject.push_back(Object("object1"));" dans le cas de cette class ce n'est pas tres grave mais imagineons que ta class object soit beaucoup plus massive alors cela n'est plus negligeable voyons donc si on peut passer par des references

                std::vector<&Object> tabObject;

                une erreure lors de la compilation se produit , essayons de comprendre nous souhaitons ajouter un object au vector

                tabObject.push_back(Object("object1"));

                si le vector prend des reference alors il vas essayer de prendre &Object("object1") mais l'object est ensuite detruit et donc la reference regarde rien la reference n'est donc pas la solution ici maintenant essayons avec des vector classique

                std::vector<Object*> tabObject;

                on ajoute des element avec 

                tabObject.push_back(new Object("object1"));
                    tabObject.push_back(new Object("object2"));
                    tabObject.push_back(new Object("object3"));

                on peut les lire 

                for (auto& object : tabObject)
                        std::cout << object->getName() << std::endl;

                si on souhaite changer l'element [n] tant que n < size()

                delete tabObject[n];
                    tabObject[n] = new Object("object4");
                

                avec cette facon de faire on cree l'object 1 fois si tu souhaite ne pas t'embeter avec les new et delete tu peut faire avec des pointeur intelligent je te laisse regarder comment ca fonctionne j'ai apris leurs existance recement :)











                -
                Edité par MathéoBrument 26 août 2023 à 14:53:19

                • Partager sur Facebook
                • Partager sur Twitter
                  26 août 2023 à 17:20:45

                  SamuelAlgeski a écrit:

                  dans ce tutorial en language C, on voit qu'il utilise des references & c'est grace à ce reference qu'on obtient  l'adresse memoire, pourquoi donc passer par pointeur ? la reference nous donnes l'adresse memoire aussi !

                  Je pense que tu confonds l'opérateur & "adresse de" avec les références. voir le post de Dalfab.

                  • Partager sur Facebook
                  • Partager sur Twitter
                  ...
                    27 août 2023 à 13:06:01

                    Salut,

                    Essayons de faire simple:

                    La notion d'adresse mémoire

                    Une adresse mémoire, c'est un peu comme l'adresse postale des gens:

                    Si tu veux rendre visite à quelqu'un (tes grands parents, ta copine, ton meilleur pote), tu dois savoir où aller pour le trouver, et tu vas donc avoir besoin d'une commune, d'une rue et d'un numéro dans la rue pour pouvoir déterminer le meilleur chemin pour y aller.

                    En informatique, c'est la même chose : toutes les données que tu déclares doivent "vivre quelque part" dans la mémoire de l'ordinateur. 

                    Mieux encore: absolument tout le matériel de ton ordinateur (ton clavier, ta souris, ton écran ou ton imprimante) doivent, pour pouvoir être utilisés, être accessible grâce à une adresse en mémoire.

                    la notion de pointeur

                    Pour pouvoir représenter l'adresse mémoire à laquelle on  va (ou plutot à laquelle on peut espérer) "quelque chose (que ce soit une donnée ou un des éléments de l'ordinateur) on va utiliser la notion de "pointeur".

                    Un pointeur n'est en réalité jamais qu'une donnée numérique entière (généralement non signée) capable de représenter l'ensemble des adresses mémoires disponibles sur ton ordinateur.

                    Mais l'avantage, c'est que cela reste une donnée "primitive" : il s'agit juste de "quelques bits" que l'on manipule pour fournir la valeur numérique qui correspond à l'adresse mémoire de ce à quoi on veut accéder.

                    Cette donnée se manipule et se copie très vite, et, surtout, ne demande aucun processus long et fastidieux lors de la copie.

                    Il est possible d'obtenir l'adresse mémoire de n'importe quelle donnée en utilisant l'espérluette & qui est dans ce cas utilisée comme opérateur "address of" (un opérateur dont le but est justement ... d'obtenir l'adresse mémoire de la donnée indiquée.

                    Le code le plus simple que l'on puisse avoir pourrait ressembler à

                    int i = 3; // on définit une donnée de type int et nommée i 
                               // dont la valeur est mise à 3 qui vit "quelque part" 
                               // dans la mémoire de l'ordinateur
                    
                    int * ptr = & i; // on définit une donnée de type 
                                     // "pointeur sur int" et nommée ptr dont
                                     // la valeur est mise à la valeur obtenue
                                     // en appliquant l'opérateur "address of"
                                     // sur la donnée i

                    Pour bien montrer que l'on a affaire à un pointeur, tu remarquera que l'on a "intercallé" une étoile entre le type de la donnée (int) et son nom (ptr).  Le type de cette donnée est donc "int *", ce qui correspond à un pointeur.

                    Le gros avantage des pointeurs, c'est que tu peux attendre un peu avant de lui faire prendre l'adresse d'une donnée comme valeur.  Tu as, cependant, intérêt à lui donner "temporairement" une valeur connue comme étant invalide en attendant ;)

                    Mieux, tu peux aussi lui faire prendre l'adresse de n'importe quelle autre donnée "quand tu veux".

                    Ainsi, un code tout à fait correct pourrait ressembler à ceci :

                    int * ptr = nullptr; // je déclare un pointeur sur int nommé
                                         // ptr et je lui donne comme valeur
                                         // une adresse connue comme étant
                                         // invalide
                    
                    /* on peut avoir plusieurs dizaines de lignes ici */
                    int i = 3;
                    /* on peut encore avoir plein de lignes ici */
                    ptr = & i; // on donne à ptr la valeur obtenue grace à 
                               // l'opérateur address of appliqué sur i
                    
                    /* à partir d'ici, ptr contient l'adresse mémoire à
                     * laquelle se trouve la donnée i
                     */
                    /* ... */
                    int j = 5;
                    /* ... */
                    ptr = &j;
                    /* à partir d'ici, ptr contient l'adresse mémoire à
                     * laquelle se trouve la donnée j
                     */
                    
                    /* NOTA: On pourrait même incrementer l'adresse représentée
                     * par ptr sous la forme de
                     * ++ptr;
                     * ce qui aurait pour effet de lui donner comme valeur
                     * l'adresse mémoire qui suit directement l'adresse 
                     * à laquelle j se trouve pour l'instant
                     */

                    Le gros problème du pointeur, c'est comme avec l'adresse de ton pote : si ton pote déménage sans te le dire, lorsque tu vas te rendre (après son déménagmenet) à l'adresse à laquelle tu crois encore qu'il habite, ben, tu risque de tomber sur "quelqu'un d'autre", ou même peut-être sur une maison vide.

                    Donc, si on en revient au pointeur, si, quand une donnée "déménage" ou qu'elle "cesse d'exister", on ne prend pas la peine de modifier l'adresse contenue par le pointeur (par exemple, pour lui donner une valeur connue pour représenter une adresse invalide, comme nullptr ou NULL), hé bien, la prochaine fois que l'on voudra accéder à "ce qui se trouve à l'adresse indiquée", on risque de tomber sur ... absolument n'importe quoi :

                    Avec "un peu de chance", l'adresse n'est simplement pas utilisée à l'instant où l'on essaye d'y accéder, mais, avec un peu de malchance, l'adresse en question aura été réutilisée pour y placer une autre donnée qui n'a absolument rien à voir avec la donnée à laquelle on espère accéder.

                    Et bien sur, si tu manipule directement la valeur représentée par le pointeur comme je le propose dans la note ( ++ptr), ben ... Va savoir ce qui se trouve effectivement à cette (nouvelle) adresse... Est-ce effectivement une donnée du type attendu? Y a-t-il seulement une donnée qui existe à cette adresse? 

                    Mais, quoi qu'il en soit, le fait d'essayer de manipuler les information qui se trouvent à cette "adresse erronnée" comme s'il s'agissait de la donnée que l'on croit y trouver va se solder par ... une catastrophe.

                    la notion de référence

                    Une référence est un "autre nom" grâce auquel on peut identifier une donnée existante.

                    Pour encore une fois faire le parallèle avec la vie réelle, nous pourrions dire qu'il s'agit d'un pseudonyme :  Par exemple, "Jhonny Halliday" était le nom sous lequel tout le monde connaissait Jean Philippe Smets : Quand "Jhonny" sortait un nouveau disque, quand il se produisait en consert ou quand on parlait de ses frasques amoureuses, hé bien c'était en réalité Jean Philippe qui faisait tout cela.

                    De même, on me connait sur ce forum sous le pseudonyme de koala01. Et je ne vais surment pas t'étonner en t'apprenant que ce n'est pas mon vrai nom.

                    Mon vrai nom est Philippe Dunski.  Et tout ce que koala01 peut écrire sur ce forum, ben, ca a en réalité été écrit par ... Philippe Dunski.

                    La grosse différence entre un pointeur et une référence tient dans le fait que la notion d'adresse mémoire est totalement oubliée lorsque l'on parle d'une référence, ce qui est normal, vu que le concept de référence est totalement différent de celui de pointeur.

                    Le code le plus simple utilisant une référence pourrait être

                    int i = 3; // on définit une donnée de type int, nommée i
                               // dont la valeur est égale à 3
                               // elle "vit quelque" par en mémoire, mais en
                               // fait, on s'en fout pas mal
                    int & ref = i;  // on défini une référence, un alias, un
                                    // pseudonyme pour la variable i auquel
                                    // on donne le nom de ref
                    /* A partir de ce point, tout ce que l'on pourra faire 
                     * subir à ref sera en réalité subit par ... i
                     * et inversément: tout ce que l'on fera subir par i 
                     * sera également constatable au niveau de ref
                     */

                    Pour bien montrer qu'il s'agit d'une référence, j'ai intercallé une esperluette & entre le type (int) de la donnée et son nom (ref).  Nous avons donc la donnée ref qui est de type "int &", ce qui en fait ... une référence.

                    Mais, attention!!! l'esperluette & que j'utilise ici n'a absolument pas le même sens que l'esperluette que j'utilisais plus haut lorsque je te parlais des pointeurs.

                    Cette différence est rendue possible parce que le symbole est utilisé dans un contexte totalement différent.

                    En essayant de simplifier au maximum, pour savoir la signification du symbole &, tu dois regarder ce qui vient tout de suite avant et ce qui vient tout de suite après.  Ce sont ces trois parties (l'élément d'avant, l'élément d'après et l'esperluette elle même ) qui détermineront le contexte dans lequel l'esperluette est utilisée et donc, sa signification ;)

                    Il y a donc trois contextes possibles:

                    • Soit, ce qui vient juste avant est un identifiant de type (int, ou MaClass, ou Type).  Dans ce cas, l'esperluette se "rattache" à cet identifiant (on a donc int &, Maclass & et Type &) et elle indique que l'on a affaire à une référence du type indiqué.
                    • Soit, ce qui vient juste avant n'est pas un identifiant de type: c'est une parenthèse ou le symbole = ou même une virgule (il y en a peut être d'autres, mais ce sont les trois auxquels je pense là maintenant).  Dans ce cas, l'esperluette se "rattache" à l'identifiant de donnée qui suit (i, dans mon exemple) et prend la signification de l'opérateur "address of" (de l'opérateur qui permet d'obtenir l'adresse mémoire à laquelle se trouve la donnée indiquée).
                    • Enfin, si ce qui vient juste avant l'esperluette est l'identifiant d'une donnée ou ( ex : i & j )-- pourquoi pas, une valeur numérique (ex:  15 & i ), alors l'esperluette prend le sens de l'opératur ET (AND) binaire, de l'opérateur permettant de représenter la conjonction en ==>algèbre booléenne<==.

                    Je ne vais pas parler de ce troisième cas, car il n'a aucun intérêt pour l'explication en cours... je ne l'ai cité que pour être complet  ;)

                    Mais bon, revenons en à nos références...

                    Le gros avantage des références, c'est qu'elle est intimement liée à l'existence de la donnée pour laquelle elle sert d'alias.

                    En effet, si tu essaye de déclarer une référence sans définir la donnée référencée.  Et, une fois que la donnée référencée a été fournie, je ne peux pas décider de faire en sorte de référencer une autre.

                    Ainsi, en reprenant l'exemple des pointeurs, je ne peux pas écrire un code proche de

                    int & ref; // je déclare une référence, mais je veux attendre
                               // avant d'indiquer la donnée référencée
                    
                    /* ... */
                    int i = 5;
                    /* ... */
                    ref = i

                    Parce que le compilateur m'engueulera au prétexte que "il faut indiquer la donnée à laquelle ref fait référence" ;)

                    Par contre, un code proche de

                    int i = 5;
                    int & ref = i; // OK : ref fait maintenant référence à la
                                   // donnée i
                    
                    /* ... */
                    int j= 15;
                    /* ... */
                    
                    ref = j;      // Ce sera accepté, mais ne fera pas forcément
                                  // ce que tu veux
                    

                    risque de ne pas faire ce que tu veux (ou ce que tu crois).

                    Mais, en fait, qu'espères tu qu'il va se passer avec ce code?

                    • Espères tu que ref soit maintenant "relié" à j, et que tout ce que tu feras subir à ref sera en réalité subi par j?
                    • Ou espères tu avoir donné la valeur de la donnée j à ref, et -- par son intermédiaire -- avoir modifié la valeur de i, tout en "gardant" i comme la donnée référencée par ref ?

                    Hé bien, dans le premier cas, tu vas être décu, car, si tu modifie la valeur de j et que tu la compare avec la valeur à laquelle ref te donne accès, tu remarqueras qu'elle n'a pas été modifiée.

                    En effet, c'est bel et bien la deuxième solution qui va survenir.  Voici un petit code qui montre de quoi je parle

                    #include <iostream>
                    
                    
                    int main(){
                        /* mes trois données: i et j sont des données de type
                         * int,
                         */
                        int i = 3;
                        int j = 15;
                        /* et ref est une donnée de type "référence sur int"
                         * qui sert d'allias pour i
                         */
                        int & ref = i;
                        /* on peut afficher les valeurs de tout ce beau monde
                         */
                       std::cout<< "Avant toute modification,\n"
                                << "i = " << i <<"\n"
                                << "j = " << j <<"\n"
                                << "ref = " << ref <<"\n";
                        /* toute modification subie par i se retrouve pour ref
                         */
                        i = 20;
                        std::cout<<"Après avoir changé la valeur de i \n"
                                << "i = " << i <<"\n"
                                << "j = " << j <<"(inchangé)\n"
                                << "ref = " << ref <<"\n";
                        /* toute modification subie par ref se retrouve pour i
                         */
                        ref = 10;
                        std::cout<<"Après avoir changé la valeur de ref \n"
                                << "i = " << i <<"\n"
                                << "j = " << j <<"(inchangé)\n"
                                << "ref = " << ref <<"\n";
                        /* ref = j change-t-il la donnée référencée ? */
                        ref = j;
                        std::cout<<"après avoir assigné j à ref \n"
                                << "i = " << i <<"\n"
                                << "j = " << j <<"(inchangé)\n"
                                << "ref = " << ref <<"\n";
                        /* les modifications apportées à ref s'appliquent-elles
                         * maintenant à j?
                         */
                        ref = 30;
                        std::cout<<"es modifications apportées à ref \n"
                                 <<"s'appliquent-elles maintenant à j?\n"
                                << "i = " << i <<"\n"
                                << "j = " << j <<"(inchangé)\n"
                                << "ref = " << ref <<"\n";
                        std::cout<<"reponse "<<((ref == j)? "oui" : "non")<<"\n";
                        return 0;
                    }

                    Et voici le résultat fourni par un compilateur en ligne ;)

                    Ce qui est tout à fait normal, parce que, à l'instar des pseudonymes, sont "intrincèquement" liées à la donnée à laquelle elle font référence.  Maintenant que Jean Philippe Smets est mort, il n'y a plus aucun espoir de participer à un nouveau concert de Jhonny (du moins, à un concer auquel il participerait "en personne") ;)

                    Quel est le but de tout cela?

                    Tout ce que j'ai pu écrire dans cette réponse jusqu'à présent ne m'a permis que de répondre à une seule question : "Qu'est ce que c'est ces notion de pointeur et de référence?"

                    Il me rest donc à répondre à une (ou deux) autre(s) question(s), bien plus importante: "A quoi cela peut il bien servir?" ou, si tu préfères "Pourquoi dois-je m'emm...der avec ces notions de pointeurs et de références ?"

                    Car les exemples que j'ai donnés jusqu'à présent nous ont permis de faire joujou avec ces notions, de comprendre comment elles foncitonnaient, mais bon, il faut avouer que, jusqu'à présent, leur utilité réelle (hormis l'aspect didactique) est proche du zéro absolu :p

                    Ce qui se passe, c'est qu'il y a une autre notion très importante en C++ : la notion de fonction.

                    Une fonction est un ensemble d'instructions qui seront traduites par le compilateur en instructions compréhensible par le processeur de l'ordinateur, et qui nous évite d'avoir à réécrire cent six fois la même chose lorsqu'il s'agit d'appliquer la même logique à différents endroits du code.

                    Ce qui est particulièrement intéressant, car, au moins, cela nous permet d'éviter les inévitables erreurs de recopie, et, surtout, de n'avoir à modifier qu'un seul endroit du code si "par malheur", le comportement observé ne correspondait pas au comportement attendu.

                    Bien sur, les fonctions vont manipuler des données (autrement, on peut se demander à quoi serviraient les instructions qui les composent ;) :D ).  Et l'on peut -- grosso modo -- classer les données que les fonctions vont manipuler en deux catégories :

                    • Les données qui sont directement connues de la fonction, que la fonction est en mesure de définir par elle-même, et que l'on appelle "variables locales" (i, j, ptr et ref sont des "variables locales" dans les exemples que j'ai donné plus haut) et
                    • les données "externes" dont la fonction a besoin pour travailler, qui doivent lui être fournie par celui qui fera appel à la fonction, et que l'on appelle "paramètre" (ou "argument", selon le point de vue d'où on se place).

                    Par exemple, je pourrais faire une fonction qui additionne deux valeur numériques entières et qui affiche le résultat de cette addition. Je peux donc créer une "variable locale" qui correspond au résultat de cette addition et décider de l'afficher.

                    Par contre, les deux valeurs qui seront additionnées, ben, il faut bien que la fonction les recoive de "quelque part". Ces données devront donc être transmises sous forme de "paramètres" lorsque l'on appellera la fonction.

                    Ce n'est pas "parfait" du point de vue conceptuel car je ne respecterais pas le principe voulant que chaque fonction ne doit faire qu'une seule chose (ben oui, cette fonction fait deux chose : additionner et afficher :D ), mais bon, cet exemple me permet d'atteindre mes objectifs en termes d'explications, donc... je vais quand même m'en servir ;)

                    Cette fonction pourrait prendre la forme de

                    /* il faut toujours donner un nom qui indique précisément
                     * ce que fait effectivement la fonction.
                     *
                     * On définit une fonction par quatre "éléments clés"
                     *   - Le type de retour (void indique ici que la fonction ne
                     *    renverra aucune valeur) 
                     *    - son nom, qui permet de savoir ce que la fonction 
                     *      va faire (ici, elle va ... Additionner et 
                     *       Afficher)
                     *    - la liste (entre parenthèses) des paramètres dont
                     *       elle a besoin,chaque paramètre étant séparé du 
                     *       suivant par une virgule
                     *     - une paire d'accolade { et } contenant les 
                     *       instructions à effectuer
                     */
                    void additionneEtAffiche( int i, int j){
                        /* je déclare une variable nommée "result" qui est de 
                         * type int
                         */
                        int result;
                        /* je donne comme valeur à result le résultat de 
                         * l'addition de i et de j
                         * (NOTA: J'aurais pu faire les deux en même temps  )
                         */
                        result = i + j;
                        /* j'affiche la valeur du résultat */
                        std::cout<< "le résultat de "
                                 <<  i << " + " << j
                                 << " est égal à " << result <<"\n";
                    }

                    Et, à partir de là, et parce que je sais que cette fonction fera toujours ce que j'attend de sa part, je peux y faire appel aussi souvent que je veux en lui donnant n'importe quelle valeur numérique entière (pour autant que l'addition des deux ne provoque pas un dépassement des valeurs possible pour le type int), par exemple, sous la forme d'un code proche de

                    int main(){
                        /* cela marche avec des valeurs numériques */
                        additionneEtAffiche( 3, 5 );
                        /* cela fonctionne avec deux variable locales de 
                         * la fonction appelante
                         */
                        int i = 15;
                        int j = 30;
                        additionneEtAffiche( i, j);
                        /* et meme avec une valeur numérique et une variable,
                         * dans un sens
                         */
                        additionneEtAffiche(i, 55);
                        /* ou dans l'autre */
                        additionneEtAffiche(33, j);
                    }

                    Allez, comme tu n'es pas obligé de me croire sur parole, ==>voici à quoi cela ressemble à l'exécution<== ;)

                    Cela a quelque chose de magique, non?

                    Sauf que, dans cet exemple, je n'ai transmis les paramètres ni par référence ni par pointeur, mais bien par valeur.

                    Et ce qui s'est passé "dans les tréfonds du processeur", c'est qu'il y a eu une copie des valeurs transmises à la fonction (j'aurais d'ailleurs pu dire que j'ai transmis les paramètres par copie au lieu de dire les avoir transmis par valeur), car même si les variables portent le même nom -- au niveau de la fonction main -- que les paramètres transmis à la fonction additionneEtAffiche  (je parle bien sur de i et de j), il s'agit vraiment de données tout à fait distinctes :

                    Si je venais à modifier i dans la fonction additionneEtAffiche, cela n'aurait absolument aucun impact sur la valeur de i au niveau de la foncion main.

                    Encore une fois, il est ici question de "contexte", car chaque fonction (main et additionneEtAffiche) représente un contexte différent, et toutes les données (i et j) sont "séparées" les unes des autres par cette notion de contexte.  Du moins, lorsqu'elles sont transmise par valeur.

                    Par contre, si j'avais transmis les données i et j de la fonction main par référence à la fonction à laquelle je fait appel (additionneEtAffiche, dans le cas présent), hé bien, le paramètre de la fonction aurait toujours été ... un alias, un "pseudonyme" de la variable utilisée comme paramètre, et ce, quel que soit le nom que j'aurais pu utiliser pour identifier le paramètre.

                    Ainsi, si j'ai une fonction qui multiplie par trois la valeur de son paramètre et qui affiche le résultat, je peux envisager deux solutions:

                    Soit, je transmet l'argument par valeur, sous une forme proche de

                    void mutiplieParTroisEtAfficheParValeur( int i){
                         i *= 3;
                         std::cout<

                    soit je transmet l'argument par référence sous une forme proche de

                    void multiplieParTroisEtAfficheParReference( int & param){
                         param *= 3;
                         std::cout<

                    Et, bien sur, je peux faire appel à ces deux fonctions d'une manière fort similaire, sous une forme proche de

                    int main(){
                       int i = 5;
                       std::cout<< "avant tout appel, la valeur de i est "
                                << i << "\n";
                       mutiplieParTroisEtAfficheParValeur(i);
                       std::cout<< "après avoir transmis i par valeur a"
                                << " la fonction, la valeur de i est "
                                << i <<"\n";
                       multiplieParTroisEtAfficheParReference(i);
                       std::cout<< "après avoir transmis i par valeur a"
                                << " la fonction, la valeur de i est "
                                << i <<"\n";
                    }

                    Et ce que l'on constate ==>à l'exécution<== c'est que la transmission par valeur ne modifie effectivement pas la valeur "originale" de la variable i qui se trouve dans la fonction main, malgré le fait que le paramètre de  mutiplieParTroisEtAfficheParValeur porte le même nom que cette variable, alors que l'appel à  multiplieParTroisEtAfficheParReference modifie la valeur de i au niveau de la fonction main, malgré le fait que le nom du paramètre utilisé par cette fonction soit différent du nom de la variable qui a été transmise.

                    Bien sur, le phénomène que je viens de présenter concernant les références est totalement identique lorsqu'il s'agit d'utiliser des pointeurs, à ceci près qu'il faudrait bien sur utiliser l'opérateur "addressOf" (l'esperluette) pour transmettre ... l'adresse de i lors de l'appel, et modifier la valeur de "ce qui est pointé" par le pointeur obtenu en paramètre en utilisant l'étoile ;)

                    Bon, c'est bien beau tout cela... Le truc, c'est qu'une fonction comme multiplieParTroisEtAfficheParReference va provoquer ce que l'on appelle "un effet de bord" en modifiant au niveau de la fonction qui y fait appel la valeur de la donnée qui lui a été transmise par référence.

                    Et le truc, c'est que " l'on n'aime pas trop ca " parce que cela rend la valeur des données beaucoup plus difficile à suivre lorsqu'on doit corriger un problème.

                    On peut donc légitimement se poser la question de savoir "mais alors, bor..el, pourquoi a-t-on besoin des pointeurs et des références?"

                    Car "Pu..in de Me...de ... si on a pris la peine de comprendre comment fonctionne les références et les pointeurs, c'est que cela doit quand même bien servir à quelque chose, non???'

                    Et je peux quand même te rassurer: oui, ces notions vont te servir à quelque chose en te permettant -- d'abord et avant tout -- d'éviter la copie de données que tu ne veux pas ou que tu ne peux pas copier.

                    La notion de pointeurs de sera, de plus, particulièrement utile lorsque l'on commencera à parler de gestion dynamique de la mémoire, mais, a priori, cela ne devrait arriver que lorsque tu abordera des concepts comme la programmation orientée objet, l'héritage et le polymorphisme.

                    Car, oui, beaucoup de cours et de tutos présentent cette notion beaucoup plus tôt que nécessaire, mais bon ... c'est un autre débat ;) .

                    Donc, pour en revenir à nos moutons, la question que tu te poses sans doute est "pourquoi voudrais-je éviter la copie d'une donnée?" et, pire encore "pourquoi ne pourrais-je pas copier une donnée?"

                    Pour répondre à la deuxième question sans trop rentrer dans les détails, disons qu'il y a certaines situations dans lesquelles on ne veut surtout pas que certaines données soient copiées, et que le langage nous offre des mécanismes qui permettent d'empêcher la copie.

                    Il s'agit de situation que l'on retrouver essentiellement en programmation orientée objet (mais pas que) et je ne vais de nouveau pas m'étendre sur ce point.  Saches donc simplement, qu'il y aura "régulièrement" des données que tu ne pourras pas transmettre par valeur (ce qui provoquerait la copie) parce que le compilateur t'en empêchera en te disant "désolé, je peux pas la copier".

                    Tu n'auras donc "pas d'autre choix" que de transmettre ces données soit par référence, soit sous la forme d'un pointeur vers la donnée qui t'intéresse.

                    Quant à répondre à la première question ("pourquoi voudrais éviter la copie?") disons que... cela peut prendre beaucoup de temps et demander parfois "énormément" de ressources.

                    Pas pour les données "simples" comme un int, un float ou autre.  Mais, dés que l'on a des données "un peu plus complexes", comme une simple std::string ou un std::vector<int>, le temps nécessaire à l'obtention de la copie peut très rapidement devenir génant.  En particulier si ces copies sont effectuées de (très) nombreuses fois d'affilée.

                    Il s'avère donc "très rapidement" largement préférable de transmettre ses données "complexes" sous la forme de référence afin d'éviter tout ce gaspillage de temps et de ressources que demanderait la copie.

                    De plus, afin d'éviter les effets de bords (je t'ai dit que l'on n'aimait "pas trop" en avoir :D ), il est toujours possible de transmettre le paramètre sous forme d'une référence constante, ce qui forcera le compilateur à t'engueuler si, par erreur, tu essaye de modifier le valeur du paramètre, et qui, par conséquent, te forcera à ne pas essayer de le faire ;)

                    PS:J'ai dit au début de mon intervention que j'allais faire "simple" ... je n'ai jamais dit que j'allais faire court ;)

                    • Partager sur Facebook
                    • Partager sur Twitter
                    Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait

                    reference et pointeur difference

                    × 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