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 :

Besoin d'un peu d'aide sur les pointeurs.


Sujet :

C

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    39
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 39
    Points : 29
    Points
    29
    Par défaut Besoin d'un peu d'aide sur les pointeurs.
    Bonjour à tous ,
    je débute en programmation et en C++ et j'ai quelques difficultées avec les pointeurs .....

    Selon ce que j'ai pu comprendre , on utilise les pointeurs avec une fonction comme 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
    16
    void triplePointeur(long *pointeurSurNombre);
     
    int main(int argc, char *argv[])
    {
        long nombre = 5;
     
        triplePointeur(&nombre); // On envoie l'adresse de nombre à la fonction
        printf("%ld", nombre); // On affiche la variable nombre. La fonction a directement modifié la valeur de la variable car elle connaissait son adresse
     
        return 0;
    }
     
    void triplePointeur(long *pointeurSurNombre)
    {
        *pointeurSurNombre *= 3; // On multiplie par 3 la valeur de la variable nombre
    }
    En gros pour utiliser mon pointeur dans ma fonction , je dois mettre * devant , sinon je travail non pas avec la valeur du pointeur , mais l'adresse en mémoire .

    Cependant je trouve beaucoup de code comme celui-ci :

    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
    21
    22
    // Prototype de la fonction d'affichage
    void affiche(long *tableau, long tailleTableau);
     
    int main(int argc, char *argv[])
    {
        long tableau[4] = {10, 15, 3};
     
        // On affiche le contenu du tableau
        affiche(tableau, 4);
     
        return 0;
    }
     
    void affiche(long *tableau, long tailleTableau)
    {
        long i;
     
        for (i = 0 ; i < tailleTableau ; i++)
        {
            printf("%ld\n", tableau[i]);
        }
    }
    Deja première constatation , on appelle la fonctionne affiche() comme ceci :
    alors que moi j'aurais plutôt vu :
    afin de passer l'adresse au pointeur .

    Ensuite , dans la fonction affiche , le pointeur tableau est utilisé sans une * devant . un peu comme on aurait pu le faire avec une référence .
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("%ld\n", tableau[i]);
    Il ne faut pas écrire plutôt ceci ? :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("%ld\n", *tableau[i]);
    Dans ce cas , quelle est la différence entre le pointeur et la référence ... ?

    Merci d'avance pour votre aide .

  2. #2
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Points : 50 367
    Points
    50 367
    Par défaut
    Je modifierais un peu ta phrase (mais j'ai l'impression que tu as compris)
    En gros pour utiliser mon pointeur dans ma fonction , je dois mettre * devant , sinon je travail non pas avec la valeur du pointeur , mais l'adresse en mémoire .
    que je remplacerai par
    En gros pour utiliser la valeur pointée par mon pointeur dans ma fonction , je dois mettre * devant , sinon je travaille non pas avec la valeur pointée, mais l'adresse de cette valeur.
    Pour ton 2eme problème, le nom d'au tableau est un pointeur sur le 1er élément de ce tableau.

    Ensuite, il faut bien comprendre que *pointeur a la même signification et la même valeur que pointeur[0].

    Tu peux utiliser l'un ou l'autre comme tu veux. Après il y a des cas où une écriture est plus naturelle que l'autre (ou a plus de sens, en général, j'utilise les [] pour montrer que le pointeur est un tableau et * pour montrer que le pointeur pointe sur 1 seule valeur). Mais encore une fois, c'est pareil.
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    39
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 39
    Points : 29
    Points
    29
    Par défaut
    Ha d'accord , je comprend mieux .
    Oui j'avais oublié ca , en fait un tableau , c'est un pointeur , donc quand on dit tableau[3] , en fait , on pointe sur la case 3 du tableau en mémoire .
    Merci bien .

    Par contre pourquoi appelle-t-on la fonction comme ceci :
    au lieu de :
    Parceque tableau est équivalent à &tableau car c'est un pointeur ?

  4. #4
    boli_971
    Invité(e)
    Par défaut
    Salut,

    Il faut bien comprendre le sens de &tableau: ça veut dire donne moi l'adresse de tableau, or tableau (sans les crochets) est déjà l'adresse du premier élément du tableau.
    En passant tableau tu passes l'adresse du premier élément du tableau.

    Pour l'information, quand tu passes &tableau tu passes l'adresse du pointeur du premier élément du tableau donc un truc qui ne sert quasiment à rien.

    Pour te faciliter la tache, comme maintenant tu sais qu'un pointeur sur un tableau n'est rien d'autre que l'adresse de son 1er élément alors tu peut écrire ton code comme suit.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    affiche(&tableau[0], 4);
    //récupère l'adresse du premier élément du tableau.
    ps: Je crois que tu t'es trompé de de forum, pas une touche de c++ dans ton code.
    En c++ il faut préférer les std::vector et le passage par référence.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Salut,
    Citation Envoyé par boli_971 Voir le message
    <snip>
    Pour l'information, quand tu passes &tableau tu passes l'adresse du pointeur du premier élément du tableau donc un truc qui ne sert quasiment à rien.
    <snip>
    [MODE AVOCAT DU DIABLE ON]
    A la base, les pointeurs ont été créés en C, non seulement pour permettre de gérer des tableaux, mais surtout pour permettre à une fonction de modifier le contenu d'une variable se trouvant dans la fonction appelante, car le C a été l'un des premiers langages dans lequel les variables n'étaient plus globales...
    Un petit code pour t'en convaincre (à compiler en 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
    void  foo(int *i)
    {
        (*i)++;
    }
    int main()
    {
        int mavar = 10;
        int cpt;
        for (cpt = 0; cpt<10;++cpt)
        {
            /* pour passer un pointeur à foo, passons l'adresse de mavar */
            foo(&mavar);
            printf("apres le passage %d dans la boucle, mavar vaut %d\n",cpt,
                   mavar);
        }
        return 0;
    }
    Et le fait de passer l'adresse d'un pointeur sert, et même régulièrement en C, quand tu veux modifier un pointeur: quand tu as affaire à un tableau dont la mémoire a été allouée dynamiquement et que tu souhaite pouvoir réallouer la mémoire, pour permettre d'augmenter ou de diminuer le nombre d'éléments que ton tableau peut contenir.

    Tu obtiens alors un pointeur de pointeur (qui n'est qu'un pointeur pointant non plus sur la donnée attendue, mais... sur un pointeur )

    Cela pourrait très bien être utilisé en C++ dans les mêmes circonstances, sous une forme proche de
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    /* fonction qui réalloue la mémoire à deux fois la taille originale
     * in / out : ptr : un pointeur sur le tableau (un pointeur de pointeur) à 
     *                  réallouer
     * in : size : la taille originale du tableau à réallouer
     * out : la taille finale du tableau (0 si l'allocation a échoué)
     */
    size_t reallocTab( Type** pptr, size_t size)
    {
        /* calculons la taille finale */
        size_t final = size*2;
        /*  déclarons un pointeur temporaire  de destination*/
        Type * temp = NULL;
        try
        {
            /* L'invocation de new peut lancer une exception std::bad_alloc
             * si le système ne trouve pas un espace contigu en mémoire
             * suffisant
             * Cela nous empêcherait alors d'avoir le tableau réalloué, mais
             * ne nous interdit pas de garder le tableau d'origine ;)
             */
            temp = new Type[final];
        }
        catch(std::bad_alloc &e)
        {
            /* Si tu veux "logger" l'erreur, c'est ici que ça se fait ;) */
            /* nous renvoyons 0 pour indiquer que nous n'avons pas
             * modifié le tableau
             */
            return  0;
        }
        /* Si nous arrivons ici, c'est que nous ne sommes pas passés dans le 
         * bloc "catch", et donc que l'allocation dynamique a réussi
         * Nous copions donc le contenu du tableau d'origine dans le tableau
         * de destination
         * comme nous avons un pointeur de pointeur, il faut travailler avec
         * "ce qui est pointé par pptr"
         */
        std::copy(*pptr,*pptr+size,temp);
        /* nous libérons la mémoire allouée au tableau  d'origne 
         * Ici aussi, il faut travailler avec "ce qui est pointé par pptr"
         */
        delete[] *pptr;
        /* Nous assignons l'adresse de temp à notre tableau... Cela modifie
         * l'adresse pointée par le pointeur  original de la  fonction appelante
         */
        pptr = &temp;
        /* Nous renvoyons la taille finale, pour indiquer que tout s'est bien 
         * passé
         */
        return final;
    }
    Cette fonction pourrait être utilisée sous une forme proche de
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    int main()
    {
        /* Nous commencons avec un tableau capable de contenir 10 éléments 
         * (taille arbitrairement choisie)
         */
        Type * tab = new Type[10];
        /* Pour manipuler ce tableau, nous avons besoin de connaitre sa taille
         * et l'index du premier élément libre de celui-ci
         */
       size_t size = 10;
       size_t firstFree = 0;
       /* Nous entrons dans une logique qui se répète */
       while (onNaPasFini)
       {
           /* Nous insérons régulièrement  des éléments dans le tableau...
            * Nous noublions pas d'incrémenter firstFree à chaque fois ;)
            * Après chaque insertion, nous vérifions s'il y a encore la place
            * pour rajouter un élément par la suite
            *
            * Si ce n'est pas le cas, nous doublons la taille du tableau
            */
           if(firstFree==size)
           {
                size_t newsize = reallocTab(&tab,size);
                /* nous vérifions que la réallocation s'est bien déroulée, si
                 * ce n'est pas le cas, nous avons peut etre quelque chose
                 * à faire (libérer de la mémoire par ailleurs et réessayer,
                 *ou décider qu'il faut sortir de l'application, selon le cas
                 */
                if(newsize == 0)
                {
                    /* que faire si la réallocation a échoué */
                }
                else
                {
                    /* si la réallocation n'a pas échoué, nous continuons avec la
                     * nouvelle taille du tableau
                     */
                    size = newsize;
                }
           }
       }
    }
    J'ai mis énormément de commentaires afin que tu puisse comprendre l'idée et donc que tu aies une chance de l'adapter à tes besoins
    [MODE AVOCAT DU DIABLE OFF]
    Ceci dit, tu l'auras compris, manipuler des pointeurs pour gérer des tableaux dont la taille évolue dynamiquement n'est clairement pas ce qu'il y a de plus facile:
    • Il faut veiller à libérer la mémoire juste au bon moment, sous peine d'obtenir des fuites mémoire (memory leaks),
    • il faut transmettre en permanence la taille du tableau pour que les fonctions soient en mesure d'éviter d'aller chipoter à une adresse mémoire qui n'appartient pas au tableau
    • Tu dois régulièrement t'assurer de garder l'index du dernier élément utilisé afin de pouvoir déterminer quand il faut augmenter la taille du tableau
    • Entre les *, les **, les &, les . et les ->, il y a franchement de quoi faire une sérieuse "soupe"
    • ...
    C'est pourquoi, le premier conseil que l'on donne, et que j'ai rappelé en moultes occasions ces derniers temps est de préférer en toute circonstance les solutions propre aux C++ à toute alternative issue du C.

    Ainsi, il vaut mieux préférer l'utilisation des conteneurs proposés par le standard, tels que vector pour les tableaux d'éléments contigus en mémoire à la gestion de pointeurs et de l'allocation dynamique (je t'invite à jeter un oeil sur la page de la FAQ qui traite de la STL et principalement sur le diagramme te permettant de choisir le conteneur le plus adapté à tes besoins)

    De même, on préférera souvent passer les structures sous la forme de référence aux fonctions plutôt que sous la forme de pointeurs, ce qui nous permet de les manipuler de manière strictement identique, sans avoir à se poser la question de savoir s'il s'agit d'une instance - impliquant d'utiliser le point "." pour accéder au champs souhaité - ou d'un pointeur , impliquant d'utiliser la fleche "->" pour y accéder.

    Au final, les cas où tu devra recourir aux pointeurs et à la gestion dynamique de la mémoire se limitera à quelques cas particuliers dont ceux où tu veux disposer du polymorphisme...

    [EDIT]Arf... à la base, la discussion était dans le forum C++...
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre chevronné
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Points : 1 750
    Points
    1 750
    Par défaut
    en fait un tableau , c'est un pointeur
    Non, justement. Ce sont deux choses différentes.

    Un pointeur, c'est une variable (un peu particulière) dont sa valeur est une adresse, et qui a la particularité de pouvoir pointer sur un objet. La valeur du pointeur ne correspond donc pas à son adresse (&pointeur).

    Un tableau c'est une suite d'éléments de même type. L'adresse du tableau, contrairement au pointeur, correspond donc à la même adresse que le premier élément.

    Maintenant, effectivement, ce qui peut être perturbant au début, c'est le fait qu'un pointeur peut s'utiliser comme un tableau, avec les crochets. Mais pointeur et tableau sont deux choses différentes.

  7. #7
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 603
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 603
    Points : 17 913
    Points
    17 913
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par jeroman Voir le message
    Maintenant, effectivement, ce qui peut être perturbant au début, c'est le fait qu'un pointeur peut s'utiliser comme un tableau, avec les crochets.
    j'aurais dit l'inverse : un tableau avec les crochets pourrait être référé (dans certains cas) comme un pointeur
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  8. #8
    Expert éminent sénior
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Points : 13 926
    Points
    13 926
    Par défaut
    boli_971 :
    Pour l'information, quand tu passes &tableau tu passes l'adresse du pointeur du premier élément du tableau donc un truc qui ne sert quasiment à rien.
    Soit un tableau défini par T tab[....], où T est le type des éléments du tableau :
    tab désigne l'objet tableau et a donc le type T[] dans les deux cas suivants
    - avec l'opérateur sizeof
    - avec l'opérateur & (adresse de)
    Il n'y a pas d'autres opérateurs qui s'applique à un objet tableau.
    Dans les autres cas, tab est une valeur qui est l'adresse du premier élément du tableau. Elle a donc le type T* .

    &tab n'est pas l'adresse du pointeur du premier...., un tel pointeur n'existe pas. La valeur de &tab est l'adresse du début du tableau, c'est à dire désigne l'emplacement du premier élément du tableau, mais son type est "adresse d'un tableau de T" : T (*)[]

    jeroman :
    Maintenant, effectivement, ce qui peut être perturbant au début, c'est le fait qu'un pointeur peut s'utiliser comme un tableau, avec les crochets. Mais pointeur et tableau sont deux choses différentes.
    Pour préciser, l'utilisation de tab[k] est une facilité syntaxique : c'est une manière agréable d'écrire *(tab+k) :
    tab[k] == *(tab+k)
    (une conséquence amusante est qu'on peut écrire k[tab] aussi bien que tab[k]).
    Elle est donc utilisable pour toute grandeur tab qui représente une adresse, pas seulement un tableau.
    Il s'ensuit que
    &t[k] == &*(t+k) == t+k
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Le problème majeur avec les tableaux, c'est qu'on ne peut pas passer vraiment un tableau par valeur en paramètre de fonction: À la place, il est implicitement converti en pointeur vers le premier élément.

    Le fait que le standard accepte qu'on déclare un tableau en paramètre d'une fonction ne fait qu'ajouter à la confusion.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Expert éminent sénior
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Points : 13 926
    Points
    13 926
    Par défaut
    Medinoc :
    Le problème majeur avec les tableaux, c'est qu'on ne peut pas passer vraiment un tableau par valeur en paramètre de fonction: À la place, il est implicitement converti en pointeur vers le premier élément.
    Oui, c'est le problème
    - mineur, une fois qu'on a compris ça, pour un tableau à une dimension puisque le pointeur obtenu est un pointeur sur un élément "simple" ;
    - très ennuyeux pour un tableau à plusieurs dimensions puisque les élements sont des tableaux. On a alors un pointeur sur un tableau dont il faut bien préciser la dimension pour définir le type du pointeur

    Le fait que le standard accepte qu'on déclare un tableau en paramètre d'une fonction ne fait qu'ajouter à la confusion.
    Tout à fait d'accord. C'est très déroutant et ambigü pour les débutants.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

Discussions similaires

  1. aide sur les pointeurs
    Par kenshin64 dans le forum Débuter
    Réponses: 3
    Dernier message: 25/06/2012, 10h05
  2. Besoin d'aide sur les pointeurs
    Par amateurc dans le forum Ada
    Réponses: 5
    Dernier message: 03/06/2008, 15h58
  3. Aide sur les pointeurs + char
    Par Deejoh dans le forum Débuter
    Réponses: 13
    Dernier message: 18/09/2007, 13h55
  4. Filemaker ... besoin d'aide sur les Plugin
    Par joange dans le forum Autres SGBD
    Réponses: 3
    Dernier message: 22/04/2004, 11h16
  5. [CR] besoin d'aide sur les formules
    Par GuillaumeDSA dans le forum Formules
    Réponses: 4
    Dernier message: 10/07/2003, 13h19

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