Introduction générale à la programmation/Pointeurs
Les pointeurs
modifierLes pointeurs sont une notion souvent mal expliqué de la programmation, autant par sa délicatesse d’utilisation ou par le fait que les débutants ont souvent du mal à comprendre à quoi cela peut servir parce que beaucoup d'enseignants l'expliquent en partant de la fin.
Concrètement, chaque langage a sa propre définition de pointeurs, mais en programmation, un pointeur est une variable destinée à contenir une adresse.
(Il est à noter qu'un pointeur peut contenir l'adresse d’un autre pointeur. Cela fait un pointeur de pointeur. Un tel besoin n’est pas extrêmement courant mais peut arriver. En particulier pour les tableaux multidimensionnels. ex: double**** vTableau; vTableau[x][y][z]= new double;)
Un moyen mnémotechnique pour comprendre et retenir les pointeurs c’est de faire l'analogie entre les pointeurs et l’expression populaire : « l'homme qui a vu l'homme qui a vu l'ours ».
L'ours est une variable (quelconque).
L'homme qui a vu l'ours est le pointeur qui sait quelle est l'adresse de la variable. Il permet donc d'aller chercher l'ours (indirectement). Si on connait l'homme, il peut nous donner l'adresse de l'ours. Donc il n'y a plus besoin de connaitre l'ours lui-même (la variable), puisqu'on peut le retrouver grâce à l'homme qui a vu l'ours (le pointeur qui connait son adresse).
Enfin, l'homme qui a vu l'homme qui a vu l'ours est le pointeur qui connait l'adresse de l'homme qui connait l'adresse de l'ours. Là encore, si l’on appelle le pointeur de pointeur, on connait le pointeur, donc l'ours. C'est-à-dire la variable. Simple, non ?
Quel est l’intérêt des pointeurs
modifier- En langage C, une méthode ne peut retourner au maximum qu'une seule valeur.
L'utilisation de pointeurs en langage c permet aux arguments (valeurs qui entrent dans une méthode) de conserver leurs modifications. Cela veut dire que grâce aux pointeurs une méthode peut retourner autant de valeurs qu'elle a d'arguments.
- Une méthode fait des copies des arguments passés par valeurs, et travaille avec ces copies. Ceci évite de modifier les valeurs des arguments par inadvertance. Cela signifie aussi que sur des arguments 'gros' (des grands tableaux par exemple), il faut que la méthode ait copié la totalité du tableau avant de travailler dessus.
Que se passerait-il si on passait en argument à une méthode une vidéo ou une encyclopédie ?
Elle ferait naturellement une copie de la vidéo ou de l'encyclopédie ... :(
Si on passait à cette méthode l'adresse (un pointeur) du premier octet de la vidéo ou de l'encyclopédie, cela serait bien plus rapide :)
Attention, on travaille directement avec les données originales. Il vaudrait donc mieux avoir une copie de ces fichiers avant d'appeler la méthode.
- En langage C le nom d'une méthode est un pointeur. On peut donc l’utiliser comme argument d'une fonction.
DONC :
- Les pointeurs servent à éviter de saturer la pile avec un bloc de données trop important. Il vaut mieux créer un tableau de 10000 chaînes de caractères en tas(gestion par la mémoire virtuelle) que de le créer dans la pile avec tous les problèmes de gestion que cela implique (Surcharge, effondrement de la pile, buffer overflow, réécriture d'adresse de retour, etc ...). De plus avec les pointeurs multiples le contrôle sur les éléments d’un tableau multidimensionnel est beaucoup plus simple et aisé que sur un tableau monolithique en pile.
- L'utilisation de pointeurs est recommandé par le "Design Pattern" "FlyWheight" qui permet d'attribuer à un nombre conséquents d'objets un nombre limité de valeurs sans surcharger la mémoire avec toutes les valeurs possibles.
- Les pointeurs permettent de s'affranchir de la notion de méthode. (n entrées et une ou zéro sortie)
- Avec l’utilisation des pointeurs, on peut avoir n entrées et n+1 sortie (+1 = return)
- On préférera utiliser des tableaux, des structures, ou mieux, des objets pour résoudre ce type de problèmes.
- En C, les paramètres sont passés par valeur.
- Le c fait une copie des arguments de la méthode, et travaille avec cette copie.
- Les pointeurs permettent de passer les valeurs par adresses (en copiant la valeur de l'adresse), donc de travailler directement sur les données avec le risque de corrompre les données.
- On peut utiliser directement le nom d'une méthode comme argument parce que c’est aussi un pointeur. C’est d'ailleurs ce qui a permis la création du compilateur objet en C++ par Bjarns STROUSTRUP en utilisant une table de virtualisation intégré à une structure et des pointeurs sur méthodes.
Exemple d'algorithme pour l’utilisation de pointeurs de fonctions
modifierOn écrit une fonction graphique.
Cette fonction dessine f.
//INITIALISATION : f est une FORME GRAPHIQUE f := "_/-\_" //. //DECLARATION DE LA FONCTION dessine_f : fonction dessine_f() dessine f fin de fonction
Cela donne le résultat :
_/-\_
Avec la fonction f qui est fixe, dessine_f() ne sait dessiner que la forme "_/-\_".
Si on remplace la valeur intrinsèque de f par un pointeur de fonction, la fonction pourra dessiner n’importe quelle forme.
Si l’on programme en C, le nom d'une fonction est un pointeur. On peut donc l’utiliser comme argument d'une fonction. cela donne quelque chose du type :
//INITIALISATION : g, h sont des FORMES GRAPHIQUES g := "/-\/-\" h := "--\/--" //. //DECLARATION DE LA FONCTION dessine_f : fonction dessine_f(P_f) dessine P_f() fin de fonction //. dessine_f(g) donnera :"/-\/-\" dessine_f(h) donnera :"--\/--"
Voir Exemple graphique (avec Gnuplot) ci-dessous.
Exemple de code pour d’utilisation de pointeurs de fonctions
modifierExemple numérique
modifier- Testé sous Code Block (Windows,Linux).
- Passer deux pointeurs de fonctions à une fonction.
- Ici on passe les deux fonctions f et g à la fonction f1_o_f2().
- La même fonction peut calculer gof, fog et fof...
- On peut remarquer que les pointeurs de fonctions ont les mêmes types arguments que les fonctions qu’ils vont recevoir.
/* ------------------------------ */
#include <stdio.h>
#include <math.h>
/* ------------------------------ */
/* ------ Fonction f ------------ */
double f(double x){return( pow(x,2.));}
/* ------------------------------ */
char feq[] = "x**2";
/* ------------------------------ */
/* ------ Fonction g ------------ */
double g(double x){return(2.0*x + 3.0);}
/* ------------------------------ */
char geq[] = "2.0*x + 3.0";
/* ------------------------------ */
/* -Fonction fog (g suivie de f)-*/
double f1_o_f2(
double (*P_f1)(double x),/* Pointeur pour la première fonction */
double (*P_f2)(double x),/* Pointeur pour la deuxième fonction */
double a
)
{
return((*P_f1)( ((*P_f2)(a))) );
}
/* ------------------------------ */
/* ------------------------------ */
int main(void)
{
double a = 2.0;
printf(" f : x-> %s\n", feq);
printf(" g : x-> %s\n", geq);
printf(" \n\n");
printf(" f(g(%.0f)) = %6.1f\n", a, f1_o_f2(f,g,a));
printf(" g(f(%.0f)) = %6.1f\n", a, f1_o_f2(g,f,a));
printf(" f(f(%.0f)) = %6.1f\n", a, f1_o_f2(f,f,a));
printf("\n\n Press return to continue.\n");
getchar();
return 0;
}
Résultat ;
f : x-> x**2 g : x-> 2.0*x + 3.0
f(g(2)) = 49.0 g(f(2)) = 11.0 f(f(2)) = 16.0
Press return to continue.
Exemple graphique (avec Gnuplot)
modifier- Testé sous Code Block (Windows,Linux).
- Passer un pointeurs de fonctions à une fonction.
- La fonction Gplt() dessine f(x) et g(x)...
- On peut remarquer que les pointeurs de fonctions ont les mêmes types arguments que les fonctions qu’ils vont recevoir.
/* ------------------------------ */
#include <stdio.h>
#include <math.h>
/* ------------------------------ */
/* --- Dessinons f et g --------- */
double f(double x){return( pow(x,2.));}
double g(double x){return(2.0*x + 3.0);}
/* ------------------------------ */
/* Le fichier de points: [a,f(a)] */
void Gplt(
double (*P_f)(double x)
)
{
FILE *fp;
double a;
fp = fopen("data","w");
for(a = -5.0; a <= 5.0; a += 0.3)
fprintf(fp," %6.3f %6.3f\n",
a, ((*P_f)(a)) );
fclose(fp);
}
/* ------------------------------ */
int main(void)
{
printf("f) Dans gnuplot -> plot \"data\" ");
Gplt(f);
getchar();
printf("g) Dans gnuplot -> plot \"data\" ");
Gplt(g);
printf("\n\n Press return to continue.\n");
getchar();
return 0;
}
Pour approfondir ce travail : Mathc Home Edition t01/Gnuplot