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 :

Comment fonctionnent les pointeurs


Sujet :

C

  1. #1
    Débutant
    Inscrit en
    Mai 2013
    Messages
    83
    Détails du profil
    Informations forums :
    Inscription : Mai 2013
    Messages : 83
    Points : 0
    Points
    0
    Par défaut Comment fonctionnent les pointeurs
    Bonjour cher LittleWhite,je vous félicite pour cette bonne explication,simple est efficace.Merci pour vos efforts et vos contributions.
    Est ce vous pouvez nous expliquer le principe des pointeurs avec votre méthode?

    Citation Envoyé par LittleWhite Voir le message
    Bonjour,

    Il n'y a pas besoin de comprendre en profondeur ce que font malloc/calloc ou autre pour pouvoir les utiliser convenablement. La réponse de jlliagre est correct. Je souhaitais juste préciser une chose :
    malloc,calloc,realloc,free, sont des appels systèmes. Cela veut dire qu'à un moment ou un autre de leur exécution, ils vont parler avec le système (le noyau) et il faut savoir que parler avec le noyau, cela prend un temps un peu plus grand, que les simples fonctions du C (non systèmes, comme sqrt, par exemple). Pour le moment, vous n'en avez que faire, du temps que cela prend, mais il faut le savoir pour plus tard (et lors de développement de programme réactif).

    Maintenant, qu'est ce qu'elle font ?

    Simplement, prenons malloc (calloc, c'est schématiquement la même chose avec un memset en plus (pour mettre la mémoire à zéro), realloc, c'est un malloc spécial, qui peut faire aussi un memcpy).
    Malloc dit :
    "Bonjour Monsieur le système, j'ai besoin de N octets"
    Le système, qui est une sorte de vieux barbu très strict et ordonné va voir dans son index permettant de savoir ce qu'il a en mémoire, si a la place et donc, s'il peut répondre à la demande. Si oui, il retourne un pointeur pour indiquer l'emplacement de la mémoire utilisable. Si non, il retourne NULL (ou il tue le programme (ouep, c'est un vieux barbu aigri))
    En théorie, on a la mémoire, le programme peut continuer tranquillement.

    Pour le free, c'est :
    "Bonjour Monsieur le système, voici la mémoire que vous m'aviez donné, j'en ai plus besoin".
    "Le système dit ok, et on continue le programme"

    Maintenant, j'ai parlé d'un index, qui trace les allocations et utilisations de la mémoire. Cet index existe vraiment et est géré par le système. En soit, c'est souvent une liste chainées, qui traque toutes les allocations. Pour chaque allocation, on sauvegarde l'emplacement en mémoire de la zone alloué, la taille, et surement le système garde aussi un identifiant pour connaître le propriétaire. (Ce qui permettra, lorsqu'un autre programme essaie d'accéder à une zone mémoire qui n'est pas à lui, de lui faire une erreur de segmentation). Après, il existe des algorithmes plus ou moins poussé, qui permettent de gérer les allocations (où les placer, comment utiliser la mémoire finalement libérée et comment gérer la fragmentation).

    Voilà, un peu plus en détails, mais c'est détails sont optionnels. Il faut juste comprendre que malloc demande de la mémoire au système et free, la libère. Un bon programme est un programme qui libère toute la mémoire qu'il a pris, c'est pour ça qu'une règle est de dire : pour un malloc, il faut faire un free.

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

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

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par etoilenord Voir le message
    Est ce vous pouvez nous expliquer le principe des pointeurs avec votre méthode?
    Je peux essayer...

    Prenons la variable float f=3.1416. Cette variable se trouve à une adresse (on va dire 0x1010) mais comme elle est de type float, elle occupe la case 0x1010 et aussi les 3 suivantes 0x1011, 0x1012 et 0x1013 (un float fait généralement 4 octets).

    Question: ai-je le droit de stocker cette valeur 0x1010 dans une autre variable ? Réponse: pas de soucis. Comme 0x1010 est une valeur entière t'as qu'à écrire int pt=0x1010.

    Question: puis-je, en utilisant pt=0x1010, récupérer le contenu la case n° 0x1010 à savoir la valeur 3.1416 ? Réponse: En théorie oui. Il suffit de passer par l'opérateur "*" signifiant "contenu de la case". Le problème c'est que la valeur 3.1416 se non pas sur la case 0x1010 mais étalée sur les 4 cases 0x1010, 0x1011, 0x1012 et 0x1013... mais avec cette instruction int pt=0x1010 il n'y a rien pour indiquer au compilo qu'il doit en ouvrir 4...

    Question: comment faire ? Réponse: il n'y a qu'à indiquer que "étoile pt" (signifiant "le contenu de la case dont la valeur est dans "pt") est un float. Donc au lieu d'écrire int pt=0x1010, on écrira float *pt=0x1010. On a notre pointeur. Et comme 0x1010 est l'adresse de f, on a qu'à l'écrire de cette façon: float *pt=&f. Ainsi on a "pt" qui est un "pointeur sur float" ou plus simplement un "float étoile", et "étoile pt" qui est un float.

    Question: j'ai une fonction avec ce prototype void fct(float *pt) et j'ai une variable float f=3.1416. Comment faire pour passer "f" à ma fonction ? Réponse: "pt" est prévu pour recevoir l'adresse d'un float, il faut donc que je passe l'adresse de f à ma fonction => fct(&f). Ainsi, pt recevant l'adresse de "f", peut aller voir le contenu de cette adresse pour y récupérer la valeur qui s'y trouve => 3.1416.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Rédacteur
    Avatar de Franck.H
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2004
    Messages
    6 951
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2004
    Messages : 6 951
    Points : 12 462
    Points
    12 462
    Par défaut
    Je pense que c'est une des explication les plus simples que j'ai vu jusque là. A l'époque où je me suis arraché la tête et cassé les dents sur les pointeurs (que j'adore aujourd'hui) j'aurais bien aimé avoir une explication de ce genre

    Je rajouterais juste (et c'est important de le savoir), c'est qu'en passant l'adresse de "f" à ta fonction fct(&f), tu peux modifier la valeur de ta variable:
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    void fct (float * f)
    {
       *f = (*f) * 2;
    }
     
    int main (void)
    {
       float f = 3.1416;
     
       printf ("f avant l'appel: %.04f\n", f);
       fct (&f);
       printf ("f apres l'appel: %.04f\n", f);
     
       return EXIT_SUCCESS;
    }
    Je voulais le préciser car c'est souvent source de bugs plus ou moins difficiles à remonter, surtout lorsqu'on ne maîtrise pas.
    Mon Site
    Ma bibliothèque de gestion des chaînes de caractères en C

    L'imagination est plus importante que le savoir. A. Einstein

    Je ne répond à aucune question technique par MP, merci d'avance !

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

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

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Franck.H Voir le message
    Je pense que c'est une des explication les plus simples que j'ai vu jusque là. A l'époque où je me suis arraché la tête et cassé les dents sur les pointeurs (que j'adore aujourd'hui) j'aurais bien aimé avoir une explication de ce genre
    Moi aussi

    Citation Envoyé par Franck.H Voir le message
    Je rajouterais juste (et c'est important de le savoir), c'est qu'en passant l'adresse de "f" à ta fonction fct(&f), tu peux modifier la valeur de ta variable:...
    Je voulais le préciser car c'est souvent source de bugs plus ou moins difficiles à remonter, surtout lorsqu'on ne maîtrise pas.
    Oui, je voulais enchainer sur "à quoi ça peut servir" et partir sur le traitement des tableaux mais j'avais pas trop le temps.

    Essayons de continuer...
    Question: A quoi cela peut servir ? Réponse: Une première utilisation d'un pointeur est de pouvoir trouver une variable et la modifier depuis une autre fonction. Quand on écrit float f=3.1416, la variable "f" n'est connue que dans le bloc où elle est définie. Et si on passe f à une fonction, celle-ci ne recevra qu'une copie de sa valeur. Or modifier une copie n'influe en rien sur l'original.
    Toutefois, la mémoire, elle, est connue de tout le programme. Et donc l'adresse 0x1010 est unique pour le programme. Il s'ensuit que si une fonction reçoit cette adresse 0x1010, elle peut tout à fait y aller et récupérer ou, mieux, modifier la valeur qui s'y trouve. Ainsi, si une fonction doit modifier une variable, il faut alors lui passer l'adresse de celle-ci. Elle recevra une copie de cette adresse, certes (qu'elle stockera bien évidemment dans un pointeur), mais même avec une copie, elle peut toujours aller à l'adresse 0x1010 et y écrire. A condition qu'elle sache bien entendu, qu'à cet endroit il faut écrire un float. Donc le pointeur de stockage sera un "float étoile". Ceci dit, si la fonction n'a pas besoin de modifier la variable alors pas besoin de l'adresse. Une simple copie de sa valeur suffit.
    Code c : 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
    #include <stdio.h>
    #include <stdlib.h>
     
    void modif(float * f)
    {
       *f = (*f) * 2;
    }
     
    void affiche(float f)
    {
        printf("valeur de f: %.04f\n", f);
    }
     
    int main (void)
    {
       float f = 3.1416;
     
       affiche(f);
       modif(&f);
       affiche(f);
     
       return EXIT_SUCCESS;
    }

    Question: Autre chose ? Réponse: Oui, le traitement accéléré des tableaux. Mais cela implique de savoir autre chose: l'arithmétique des pointeurs. Celle-ci est toute simple: si j'écris float *pt=&f alors pt reçoit la valeur 0x1010. Et si j'affiche pt (juste la valeur, pas le contenu de la case pointée), j'afficherai alors 0x1010.
    Mais si j'incrémente pt (pt++), alors pt ne vaudra pas 0x1011 mais 0x1014. En effet, pour le compilo, *pt désigne un float donc 4 octets. Et donc les 4 adresses 0x1010, 0x1011, 0x1012 et 0x1013 sont toutes attribuées au float. Et donc l'adresse suivante sera la première disponible donc 0x1014. Et si celle-ci désigne aussi un float, alors la suivante sera 0x1018. Et etc etc etc.
    Et avec ça, on peut optimiser le traitement des tableaux.
    Prenons un tableau de float: float tab[]={3.1416, 2.71828, 1.61803}. A ce niveau là, il est nécessaire de savoir que ces 3 valeurs sont contigües en mémoire (une certitude garantie par la norme).
    Maintenant, si on veut afficher les 3 valeurs, on peut partir sur une boucle classique et afficher tab[x]
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int i;
    for (i=0; i < 3; i++)
        printf("%f\n", tab[i]);
    C'est tout à fait correct et suffisant dans la majorité des cas. Mais chaque appel tab[x] impliquera de la part du compilo un calcul sur tab décalé de x positions. Ce n'est pas généralement dérangeant sauf si tab[x] est invoqué plusieurs fois. A ce moment là, les calculs répétés peuvent générer un certain malaise chez les programmeurs qui connaissent ce détail.
    Pour pallier à ce problème, au lieu de demander de redécaler à chaque fois à partir de tab, il suffit de mémoriser l'adresse de l'élément en cours dans un pointeur puis d'afficher la valeur contenue à cette adresse. Ensuite, on décale le pointeur d'un cran et, avec son arithmétique bien à lui, celui-ci se place automatiquement sur la valeur suivante du tableau (puisque celle-ci est située juste après la précédente).
    Ce qui donnera
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int i;
    float *pt;
    for (i=0, pt=&tab[0]; i < 3; i++, pt++)
        printf("%f\n", *pt);
    Enfin il faut savoir que le nom brut d'un tableau correspond à l'adresse de son premier élément, autrement dit que &tab[0]=tab ce qui permet d'écrire
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int i;
    float *pt;
    for (i=0, pt=tab; i < 3; i++, pt++)
        printf("%f\n", *pt);

    Autre chose ? Réponse: Oui, pas mal. Quand on manipule des éléments complexes (comme des structures) et qu'on veut la passer à une fonction, il vaudra mieux passer l'adresse de la structure (2 ou 4 octets) plutôt que la structure elle-même. Car rappelons-le, tout passage d'un élément à une fonction implique la recopie de cet élément dans la fonction. Et vaut mieux copier 2 ou 4 octets qu'une structure de peut-être plusieurs centaines/milliers d'octets. On pourrait aussi parler des pointeurs de fonctions qui permettent de faire des traitement généralistes avec le code spécifique au traitement final écrit autre-part (code ainsi interchangeable) mais si on comprend les bases expliquées ici, on n'aura alors aucun soucis pour ensuite apprendre et comprendre par soi-même l'utilisation de cet outil si particulier...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

Discussions similaires

  1. Comment fonctionnent les "keywords"
    Par shikakus dans le forum Référencement
    Réponses: 3
    Dernier message: 29/01/2007, 00h13
  2. Comment fonctionne les versions d'un logiciel?
    Par Antigonos Ier Gonatas dans le forum Windows
    Réponses: 12
    Dernier message: 14/07/2006, 18h48
  3. [VB6] Comment fonctionne les Tableaux ?
    Par Lucas42 dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 27/04/2006, 14h59
  4. [FLASH 8] Comment fonctionne les clips
    Par steeves5 dans le forum Flash
    Réponses: 3
    Dernier message: 27/01/2006, 10h23
  5. Comment fonctionnent les index des options d'un select ?
    Par pekka77 dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 31/10/2005, 18h05

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