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 :

Question sur les pointeurs


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    13
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 13
    Par défaut Question sur les pointeurs
    Bonjour,

    J'ai une question à propos de l'utilisation des pointeurs.

    J'ai une méthode définie comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Classe1::GestionTAB1(float* tab)
    {
    tab[0]=1;
    }
    Elle est utilisée de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void main()
    {
    float* tableau;
    int i=50;
    tableau=new float[100];
    Classe1::GestionTAB1(&tableau[i]);
    //cela a pour effet : tableau[50]=1; je crois
    }

    Maintenant, j'ai une nouvelle méthode définie comme cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Classe1::GestionTAB2(float*& tab)
    {
    tab[0]=1;
    }
    Je voudrais remplacer la méthode GestionTAB1 dans main par la méthode
    GestionTAB2
    J'ai essayé :
    Classe1::GestionTAB2(tableau); ca marche mais ca a pour effet : tableau[0]=1; alors que je voudrais avoir tableau[50]=1;

    J'ai donc essayé : Classe1::GestionTAB2(&tableau[i]); mais ca ne compil pas.
    de meme Classe1::GestionTAB2(tableau + i); ne compil pas.

    Comment faire?
    Je voudrais passer comme argument à la méthode GestionTAB2, le tableau "tableau"
    à partir de l'indice i.

    Merci de votre aide

  2. #2
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 131
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 131
    Billets dans le blog
    150
    Par défaut
    Il est entièrement normal , que GestioTAB2 ne compile pas.

    l'etoile '*' indique que t'utilise un pointeur ( une adresse mémoire ).
    le et commercial '&' indique que tu veux le pointeur d'une valeur ( donc l'adresse mémoire ).

    Si je fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    int i = 5;
    int* pi = &i;     // Précisément , j'indique que je veux un pointeur sur un int , qui prendra la valeur de l'adresse mémoire de i.
    *pi = 6;       // Donc i = 6 , car nous avons modifié la valeur pointer par pi , qui est tout simplement i.
    Bon je crois que j'ai assez fait d'exemple. Retournons au problème.

    Je trouve que la fonction GestionTAB1 est assez bizarre ( surtout l'utilisation ).
    Une meilleure solution serait de passer à la fonction GestionTAB , l'indice de l'élément à mettre à zero ; comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    void GestionTAB1(float* tab, int i)
    {
    tab[i] = 0;
    }
    Bref , la deuxième fonction ne compile pas , car tu veux en paramètre, ... en fait je ne sais pas trop. C'est bizarre ... ça ne se fait pas , ou alors que quelqu'un m'explique.
    Comme dans le main tu vas passer un pointeur ( grace à : &tableau[50] ) et qu'il s'attend à avoir un float* , et comme tu as rajouter un autre '&' , il va croire qu'il va recevoir un float** ... Mais je n'en suis pas sur. Et de plus je peux te dire que c'est vraiment pas un bon truc.
    Explique nous plutot ce que tu veux faire avec ta fonction 2
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  3. #3
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void GestionTAB2(float*const& tab)
    {
    tab[0]=1;
    }
    En fait, c'est un peu plus compliqué que ce qui a été précédemment dit. L'explication est subtile. J'essaie de formuler ça un peu plus tard.

    [EDIT]:Mais pourquoi vouloir passer une référence sur le pointeur?

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    13
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 13
    Par défaut
    Bonjour et merci pour vos réponses

    En fait ce n'est pas moi qui est modifié la méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Classe1::GestionTAB1(float* tab)
    en

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Classe1::GestionTAB2(float*& tab)
    je ne comprend pas le terme float*&, je me demande si ce n'est pas une erreur ... Je ne connais pas cette notation (*&).

    Si quelqu'un pouvais m'expliquer...

    Merci d'avance

  5. #5
    Membre éprouvé Avatar de krieg
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    75
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 75
    Par défaut
    Bonjour,
    Le passage de parametre avec int *& T, est en réalité une simple référence.
    Lors de la modification de la variable T, la valeur du parametre passé est modifié aussi au retour de l'appel de la fonction.
    Si je n'ai pas été claire, cela est bien expliqué ici:
    http://cpp.developpez.com/cours/polyCpp/

  6. #6
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Effectivement, c'est ce qui a été dit précédemment: * et & sont indépendants.
    Si on reprend les bases:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    void Methode(Type*parametre);
    Ici, on dit que le paramètre passé à Methode est une adresse. L'appel devra donc fournir une adresse et se fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Type valeur;
    Methode(&valeur);
    Ici, l'opérateur& permet de récupérer l'adresse de la variable valeur.
    Pour un tableau :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Type tableau[NbrElts];
    L'adresse d'un tableau peut s'obtenir de 2 façons :
    1. Soit directement en utilisant le nom de la variable tableau. Celle-ci correspond en effet à l'adresse du premier élément du tableau:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
       
      Type tableau[NbrElts];
      Methode(tableau);
    2. Soit en récupérent l'adresse d'un élément du tableau. Par l'opérateur [], on déréférence le tableau sur une cellule donnée et ensuite on utilise l'opérateur & pour récupérer l'adresse de cette cellule:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
       
      Type tableau[NbrElts];
      Methode(&tableau[i]);
      // Ecriture équivalente (lié à l'arithmétique des pointeurs):
      Methode(tableau+i)

    Tu peux trouver un tutoriel sur les pointeurs. Il a été écrit pour le C mais reste valide pour le C++.

    Pourquoi passer un paramètre par pointeur ?
    1. Modifier la valeur du paramètre dans la méthode,
    2. Eviter la copie de l'objet sur la pile surtout s'il s'agit d'un objet important en mémoire (adjoint alors du qualificatif const si besoin),
    3. Passer un tableau (adjoint alors du qualificatif const si besoin).

    En C++, pour atteindre les objectifs 1 et 2, on n'utilise que rarement les pointeurs et on privilégie les références. Par parenthèse, l'objectif 3 est souvent contourné par l'utilisation des conteneurs en lieu et place des tableaux en brut.

    Maintenant examinons dans la méthode les deux utilisations possibles du paramètre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    void Methode(Type *parametre)
    {
         if(parametre!=NULL){ // Utilisation 1
               *parametre = VALEUR; // Utilisation 2
         }
    }
    Dans la première utilisation, on travaille sur l'adresse donnée en paramètre. Dans la seconde utilisation, on travaille sur la valeur. Quelles sont les différences? L'utilisation 1 permet de connaître l'adresse du paramètre, c'est à dire l'emplacement mémoire où se trouve la variable qui a été fournie en paramètre au moment de l'appel. L'opérateur de déréfencement * dans la seconde utilisation permet de dire que l'on ne veut pas travailler sur cette adresse mais sur le contenu qui se trouve dans cette adresse. Donc *parametre permet de lire et de modifier ce contenu.

    Enfin, dernier point, une adresse est une valeur particulière qui désigne un emplacement mémoire. Les lignes suivantes sont valides :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Type *adresse;
    adresse = NULL;
    adresse = 42;
    Le premier cas est bien connu, puisqu'on utilise NULL pour signifier que l'adresse n'est pas valide.
    Le second cas est aussi correct du point de vue du langage, mais risque de planter à l'exécution car 42 n'est pas forcément une adresse valide. Il peut exister des sytèmes dans lesquels cette adresse est valide. C'est pourquoi, on peut très bien écrire des valeurs en dur pour des adresses. Mais, ce sont des cas très particuliers sur des systèmes bien maîtrisés.

    Donc, venons-en aux référence. Le caractère & peut aussi servir à déclarer une référence. Il s'agit d'une variable particulière qui ne contient pas de valeur en propre mais qu'on lie à une autre variable. Cela peut se concevoir comme un synonyme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Type variable;
    Type &reference = variable;
    reference est un synonyme de variable. Utiliser variable ou reference est strictement identique. Elles ont les mêmes valeurs et les mêmes adresses :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    ASSERT(variable==reference);
    ASSERT(&variable==&reference);
    Note que le mot-clé & a deux significations complètement différentes entre les deux bouts de code précédent. Lorsqu'on écrit Type&reference, le mot-clé & sert à dire que le type de reference n'est pas Type mais une référence sur Type. Alors, que lorsqu'on écrit &variable ou &reference, le mot-clé & agit comme un opérateur qui sert à récupérer l'adresse de la variable. Il faut comprendre: même signe mais rôle différent !

    Deuxième remarque, une référence est forcément initialisée à sa déclaration et ne peut être modifiée ensuite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Type variable;
    Type &reference = variable;
    Type &reference2; // Interdit -> erreur à la compilation
    Type variable2;
    reference = variable2; // reference est toujours synonyme de variable, seul la valeur a été modifiée:
    ASSERT(reference==variable);
    ASSERT(&reference==&variable);
    ASSERT(&reference!=&variable2);
    Enfin, contrairement aux adresses, les références n'ont pas de valeur en elle-même : elle ne désigne pas un emplacement mémoire. On ne peut pas écrire des choses comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Type &reference = NULL; // Erreur de compilation.
    Type &reference = 42; // Erreur de compilation.
    On peut utiliser une référence comme paramètre d'une méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Methode(Type& parametre);
    L'appel est alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Type valeur;
    Methode(valeur);
    Et l'utilisation est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    void Methode(Type& parametre)
    {
       parametre = VALEUR;
    }
    Pourquoi utilises-t-on une référence : ce sont les éléments 1 et 2 présentés ci-dessus : pour permettre de modifier la valeur de l'argument et/ou pour éviter de construire une copie de l'argument sur la pile.

    Rien n'empêche de mélanger les deux comme tu l'as fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Classe1::GestionTAB2(float*& tab)
    Cela signifie que tab est une référence sur une adresse du type float.
    Donc la ligne de code suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    float monTableau[N];
    Classe1::GestionTAB2(tableau);
    Le paramètre tab est donc un synonyme sur tableau, les deux étant un pointeur sur float.
    Venons-en à la question: pourquoi le code suivant ne fonctionne pas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    float monTableau[N];
    Classe1::GestionTAB2(&tableau[i]);
    &tableau[i] permet d'obtenir l'adresse tableau + i. Or comme on ne peut pas initialiser une référence avec une valeur, la ligne ne compile pas.
    Ce qu'il faut comprendre c'est qu'en écrivant GestionTAB2(tableau), tab devient synonyme de tableau. Alors qu'en écrivant GestionTAB2(&tableau[i]), on fait d'abord une opération &tableau[i] qui nous renvoie une adresse. Et, on ne peut lier une référence avec une donnée brut (l'adresse).

    La question devient alors : mais pourquoi la ligne suivante permet de résoudre le problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    void GestionTAB2(float*const& tab);
    // Puis
    Classe1::GestionTAB2(&tableau[i]);
    En fait, en adjoignant const, on permet au compilateur de créer une variable temporaire de type float*, de l'initialiser avec l'adresse &tableau[i] et on lie alors tab à cette variable temporaire. const est important ici en disant qu'on ne va pas modifier l'adresse, on permet la création de cette variable temporaire.

    Bon, je ne sais pas si c'est suffisamment clair, mais pour approfondir, le mieux est encore de consulter une tutoriel ou un ouvrage sur les références et le C++

Discussions similaires

  1. Question sur les pointeurs génériques
    Par mikedavem dans le forum C
    Réponses: 16
    Dernier message: 24/05/2006, 11h56
  2. question sur les pointeurs
    Par jd.baculard dans le forum Langage
    Réponses: 3
    Dernier message: 18/03/2006, 02h30
  3. [Debutant] Nouvelle question sur les pointeurs
    Par etiennegaloup dans le forum Débuter
    Réponses: 3
    Dernier message: 11/01/2006, 09h55
  4. Question sur les pointeurs.
    Par Chrisemi dans le forum C++
    Réponses: 5
    Dernier message: 28/10/2005, 23h47
  5. questions sur les pointeurs
    Par Hyoga dans le forum C++
    Réponses: 17
    Dernier message: 08/01/2005, 23h25

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