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 :

Je n'arrive pas à comprendre les "pointeurs"


Sujet :

C

  1. #1
    Membre du Club
    Inscrit en
    Juillet 2006
    Messages
    242
    Détails du profil
    Informations forums :
    Inscription : Juillet 2006
    Messages : 242
    Points : 56
    Points
    56
    Par défaut Je n'arrive pas à comprendre les "pointeurs"
    Bonjour à tous,

    Qui est-ce qui peut m'expliquer clairement sans ambiguité le concept de pointeur ? En plus dès qu'on parle de "pointeur de pointeur" alors c'est vraiment l'incompréhension totale !

    merci beaucoup

  2. #2
    Membre éprouvé
    Avatar de mitkl
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2010
    Messages : 364
    Points : 1 081
    Points
    1 081
    Par défaut
    Salut,

    si tu es à l'aise avec l'anglais tu peux regarder ces vidéos effectuées par l'université de stanford : http://cslibrary.stanford.edu/104/, tu comprendras en gros comment les mettre en place. Sinon, j'imagine que tu t'es déjà documenté sur le sujet, qu'est-ce que tu ne comprends pas exactement dans les pointeurs ?
    Si vous ne savez toujours pas ce qu’est la récursivité, relisez cette phrase.

    Mon blog sur la programmation et l'informatique !

  3. #3
    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
    Avant de s'attaquer au concept de pointeur de pointeur, pourquoi ne pas commencer par celui de pointeur tout court hein ? Le principe de pointeur pourra être étendu à celui de pointeur sur pointeur, puis à celui de pointeur sur pointeur de pointeur, et ainsi de suite.

    Plusieurs points à comprendre.

    1) Quand tu crées une variable, elle est stockée à un endroit précis en mémoire. Comme quand tu ranges un objet dans une étagère comme celle-ci, ton objet est à un endroit donné, du genre 1er étage, compartiment 4.

    C'est le cas pour toute variable que tu crées : elle a une adresse bien définie, accessible grâce à l'opérateur unaire &.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int a =2; // variable
    &a; // adresse de a
    2) Imaginons maintenant que ton espace mémoire est très grand, bien plus grand que la petite étagère que je t'ai montré. Ça serait donc bien utile d'avoir une seconde variable qui contiendrait l'adresse de ton objet (a, dans le code exemple). C'est utile surtout en C pour passer l'adresse d'une variable à une fonction (pour des raisons de taille de pile mais surtout pour pouvoir modifier le contenu de la première variable. Ce n'est pas possible en la passant directement à cause du principe de "passage par copie des paramètres" mais ceci est une autre histoire).


    Cette seconde variable est un pointeur. Un pointeur contient l'adresse d'une autre variable. Si la première variable est à l'adresse "1er étage, compartiment 4", alors le pointeur est égal à "1er étage, compartiment 4".

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int pointeur= NULL; // je crée un pointeur
    pointeur = &a; // il contient l'adresse de a
    3) Pour des raisons de cohérence, quand tu souhaites stocker l'adresse d'un int, tu crées une variable de type pointeur sur int. Si c'est pour stocker l'adresse d'un char, tu crées une variable de type pointeur sur char.

    4) Si tu as saisi cela, tu peux étendre le concept en créant une 3e variable, qui contient l'adresse de la seconde variable (qui elle même contient l'adresse de la première variable). En effet, toute variable a une adresse même les variables de type pointeur sur int (ou sur char, float...).

    Cette 3e variable est un pointeur puisqu'elle contient l'adresse d'une autre variable (la 2e).

    Comme cette seconde variable est elle-même un pointeur, on dit donc que la 3e variable est de type pointeur sur pointeur sur int.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int ** pointeurDePointeur = NULL;
    pointeurDePointeur = &pointeur;

  4. #4
    Membre du Club
    Inscrit en
    Juillet 2006
    Messages
    242
    Détails du profil
    Informations forums :
    Inscription : Juillet 2006
    Messages : 242
    Points : 56
    Points
    56
    Par défaut
    Quand est-ce qu'on doit alors utiliser un pointeur et quand est-ce qu'on n'en utilise pas ?

  5. #5
    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
    As-tu déjà compris le principe d'un pointeur ?


    La FAQ donne des réponses : http://c.developpez.com/faq/?page=pointeurs

  6. #6
    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
    Voilà une présentation rapide qui peut être éclaircira quelques points.

    A- Adresses :

    1- En C un objet (~variable) est placé quelque part en mémoire et occupe une zone contigüe de cases mémoires.

    2- L'endroit en mémoire où se trouve un objet est repérée en C par une valeur appelée ici son "adresse C" ou simplement "adresse". Nous n'avons pas à savoir par quel mécanisme se fait la correspondance "adresse C" <-> position mémoire effective de l'objet
    L'adresse C de l'objet Obj est obtenue par l'opérateur (unaire) & : &Obj.
    L'objet situé à l'adresse C Adr est obtenu par l'opérateur * : *Adr.

    3- Une adresse C a un type ce qui permet d'avoir des informations sur l'objet dont elle est l'adresse (par exemple l'étendue mémoire réservée pour cet objet).
    Si l'objet Obj est de type T, son adresse Adr a pour type "adresse d'objet de type T" ce qui s'écrit en C T*.
    Par exemple, le type int * de Adr permet de savoir que l'objet *Adr a le type int
    Si l'adresse ne spécifie pas le type de l'objet, elle est du type void *.

    4- Certains opérateurs sont définis sur les adresses :
    - les opérateurs de comparaisons
    - les opérateurs + et - avec pour opérandes une adresse et un entier. le résultat est une adresse
    - l'opérateur - avec pour opérandes deux adresses. le résultat est un entier
    (on ne détaillera pas ici leur fonctionnement)

    5- Il existe une adresse, identifiée par NULL, qui n'est l'adresse d'aucun objet.
    B- Objet pointeurs :
    1 - Il existe donc des valeurs "adresses" (associées aux objets et décrites précédemment).

    2 - Il est naturel de vouloir les stocker quelque part. Il fait donc définir des objets qui stockent des adresses. Ces objets sont des pointeurs. Si l'objet stocke des adresses d'objets de type T, il sera déclaré du type "pointeur sur objet de type T" soit en C T*.

    Ceci utilise le même principe syntaxique que pour des grandeurs plus communes comme les int :
    Il existe des valeurs de type int. Elles peuvent être stockées dans des objets et ces objets sont dits de type int. On confond donc, pour simplifier, deux choses différentes en un même vocable "int" qui peut désigner selon le cas une valeur entière (comme 3 est un int) ou un objet stockant un entier (comme dans int i; où on dit que i est un int)
    Ceci ne pose aucun problème alors que, bizarrement, pour les adresses, confondre les valeurs adresses et les objets qui les stockent, pointeurs, en un même terme "pointeurs" pour désigner deux choses différentes engendre des difficultés de compréhension pour les débutants. Il vaut mieux donc les distinguer dans le discours.

    3- Evidemment, ces objets ont une adresse. Par exemple, conformément à §A-3, si Obj est un objet "pointeur sur objet de type int", donc du type int*, son adresse &obj a pour type int **. Si on veut stocker cette adresse, il faut un objet "pointeur sur objet de type int *" donc de type int** (§B-2)

    C'est tout ce qu'il y a à dire sur les pointeurs. Il n'y a rien à ajouter. Tout ce qu'il faut savoir concerne les propriétés des adresses.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

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

  7. #7
    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
    Citation Envoyé par andrianiaina Voir le message
    Quand est-ce qu'on doit alors utiliser un pointeur et quand est-ce qu'on n'en utilise pas ?
    On utilise un pointeur lorsqu'on veut stocker une adresse.

    La bonne question est "quand utilise t'on une adresse?"

    Pour accéder à un objet (lecture ou écriture), il faut savoir où il est, donc connaitre son adresse. Il y a plusieurs cas :

    - Un identificateur a été attribué à l'objet. Alors, le compilateur sait où se trouve l'objet et fait lui-même la liaison entre l'identificateur et l'adresse de l'objet. Le passage par l'adresse est alors transparent pour le programmeur. Mais, on peut aussi accéder à l'objet directement à partir de son adresse (obtenue par l'opérateur unaire &).

    - L'objet n'est connu que par son adresse; il n'a pas d'identificateur; c'est un objet anonyme. Il y a deux cas classiques (en C99 il en existent d'autres)
    - l'objet est obtenu par allocation dynamique : exemple int * p = malloc(...).
    - Dans ce type d'expression const char * p = "abcde"; où l'objet tableau qui contient la chaine "abcde" n'a pas d'identificateur associé
    - l'objet est un tableau passé en argument d'une fonction. En fait, c'est l'adresse du premier élément du tableau qui est passé à la fonction et est stockée dans le paramètre de la fonction (qui est donc un pointeur). Dans la fonction, le tableau n'est plus connu que par son adresse de départ, il est devenu en quelque sorte anonyme pour la fonction.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

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

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

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

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 375
    Points : 23 634
    Points
    23 634
    Par défaut
    Citation Envoyé par andrianiaina Voir le message
    Qui est-ce qui peut m'expliquer clairement sans ambiguité le concept de pointeur ? En plus dès qu'on parle de "pointeur de pointeur" alors c'est vraiment l'incompréhension totale ! merci beaucoup
    C'est plus simple lorsque l'on sait comment fonctionne un micro-processeur à la base. Voici le brochage physique d'un Z80 (un huit bits très répandu dans les années 1980) :

    http://en.wikipedia.org/wiki/File:Z80_pinout.svg

    Que voit-on ?
    • Que le micro-processeur est en soi un produit indépendant, à la base, de la machine dans laquelle il est installé ;
    • Qu'à part l'alimentation, l'horloge et les lignes de contrôles (notamment les IRQ), un micro-processeur n'est doté que d'un bus d'adresse et d'un bus de données ;


    Adresse mémoire :

    Concrètement, ça veut dire que le micro-processeur ne sait faire que trois choses : lire un octet à un endroit donnée, effectuer un calcul logique ou arithmétique dessus, et redéposer cet octet à un autre endroit.

    Et justement, cet « endroit » est un numéro de case mémoire. Le bus d'adresse est donc un ensemble de n lignes (ici 16) qui sert uniquement à coder un nombre entier en binaire naturel. Ce nombre est le « numéro » de l'octet que le micro-processeur souhaite référencer. Chaque octet en mémoire a donc un numéro, que l'on nomme adresse.

    Du côté du logiciel, le micro-processeur « voit » donc un plan mémoire composé de n octets consécutifs, et c'est tout. Et toute la ROM, la RAM, la RAM vidéo et les ports d'entrées-sorties sont placés dans ce plan : la totalité de l'exploitation d'un ordinateur se fait donc en lisant et en écrivant dans ce plan. Y compris la gestion des périphériques, comme la gestion d'un disque dur ou du port parallèle. C'est aussi par le même moyen que ton micro-processeur va lire le code à exécuter.

    Note qu'ici, le bus d'adresse n'a que 16 lignes, ce qui fait 65536 combinaisons différentes. Le Z80 ne voit donc que 64 Ko de mémoire consécutive. Pour disposer de plus de mémoire, il faut recourir à de la mémoire paginée.

    Comparé au C, tu peux considérer la mémoire visible comme un grand tableau : « char[65536] ». L'adresse mémoire correspond alors à un index dans ton tableau.

    Pointeur :

    Le C est un langage très proche de ta machine. Bien que tous les concepts soient définis pour pouvoir en faire abstraction si nécessaire, en pratique, les adresses que tu vas manipuler seront des adresses réelles. C'est pour cela qu'un programme C plante si tu fais des bêtises avec. Le code généré est fait pour être directement interprété par le micro-processeur et pas par une couche sous-jacente.

    Puisqu'une adresse est un nombre entier, le premier réflexe consisterait à stocker les adresses dans des int, ce que font d'ailleurs plusieurs autres langages. Mais le C propose mieux : un type « pointeur », c'est-à-dire une variable dont le contenu est officiellement une adresse en mémoire.

    C'est pratique parce que le compilateur va automatiquement utiliser le format d'adresse qui correspond à l'architecture pour laquelle il compile : sur un PC 32 bits, les pointeurs sont naturellement larges de quatre octets. Sur les 64 bits récemment démocratisés, un pointeur tient sur 8.

    Maintenant, cette adresse mémoire est en général celle de quelque chose. D'où le nom : un pointeur « pointe » un objet en mémoire parce qu'il t'en donne son emplacement.

    Par exemple, quand j'écris « int * x; », « x » est un pointeur sur un entier. C'est-à-dire qu'il contient l'emplacement en mémoire d'un entier. Tu peux donc soit faire référence à l'adresse proprement dite, en utilisant le pointeur comme n'importe quelle autre variable, donc en écrivant « x », soit faire référence à l'objet pointé, en « déréférençant » le pointeur : « *x ».

    Or, si un pointeur est une variable, celle-ci est donc déposée à un certain endroit de la mémoire également, et a donc elle-même une adresse. Et si je peux pointer un entier, je peux tout-à-fait pointer un autre pointeur. Cet autre pointeur pointe également quelque chose, par exemple un entier. Donc, je peux très bien avoir un « pointeur sur un pointeur sur un entier » : « int ** x ». Il n'y a pas de limite au nombres d'indirections que tu peux induire : « int ************* x; » est une expression valide, par exemple.

    À quoi cela sert-il ?

    À tout :

    • Tu réserves de la mémoire : le système doit bien te dire quelle partie de la mémoire il t'a allouée pour que tu puisses l'utiliser. Il te renvoie donc un pointeur vers le début de la zone accordée ;
    • Tu utilises une chaîne de caractères. Il s'agit en fait d'une suite de caractères individuels déposés en mémoire les uns à la suite des autres. On va donc te donner l'adresse en mémoire du premier caractère pour que tu puisses en déduire celle des suivants ;
    • Tu veux faire un tableau de chaîne :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      char const * const tableau[] = { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
      Chaque chaîne va se traduire par un pointeur vers des données en mémoire. Tous ces pointeurs sont réunis dans un tableau. Ce tableau étant une suite consécutives d'éléments de même nature en mémoire (comme une chaîne), donc, on va donc te renvoyer un pointeur sur le début du tableau. Donc, « un pointeur sur une liste de pointeurs sur des chaînes de caractères » : « char ** ».

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

Discussions similaires

  1. Réponses: 3
    Dernier message: 18/11/2005, 12h27
  2. [C#] Je n'arrive pas à remonter les données
    Par Le Basque dans le forum Windows Forms
    Réponses: 14
    Dernier message: 17/01/2005, 19h40

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