[TUTO] Les Pointeurs

Informations pour bien débuter en PureBasic
Torp
Messages : 360
Inscription : lun. 22/nov./2004 13:05

[TUTO] Les Pointeurs

Message par Torp »

Voici donc une compilation et une remise en forme des conversations que nous eu avec Nico, sur l'utilisation des pointeurs. Il y a pas mal de copié/collé des explications de Nico, auxquelles j'ai ajouté 2 illustrations (je comprends mieux avec des dessins... :) ), plus 2 ou 3 exemples et commentaires de mon cru... Bonne Lecture !

Il y a deux possibilités pour accéder à une variable
- par son nom, l'adressage immédiat
- par son adresse, l'adressage indirect

L’utilisation des pointeurs est liée à la mémoire puisqu'un pointeur est une variable spéciale qui contient toujours une adresse mémoire.

Toutes les variables d'un programme sont contenues en mémoire dans des cases que l'on nomme adresses. A une adresse correspond un octet, une variable occupera un ou plusieurs octets suivant son type. Il faut bien différencier le contenu d'une variable avec son adresse.

Exemple: nombre.w=256 nombre ici est de type word, il est donc définit sur deux octets mais son adresse mémoire est définit sur 4 octets (système window 32bits).

Cela signifie que la variable nombre occupe deux adresses mémoires contenant chacune 1 octet. Une variable peut donc occuper plusieurs adresses mémoires d'octets contigus.

Image

Connaissant le type d'une variable, seul nous est nécessaire de connaitre la première adresse d'une variable contenant l'octet de poids faible.
la représentation de 256 sur deux octets donne 0 pour le premier et 1 pour le deuxième.

L' adresse mémoire d'une variable ne change pas au cours d'un programme, elle reste la même pendant toute la durée de vie de la variable; c'est Windows qui la détermine à chaque lancement du programme.

Les variables numériques:

Soit une variable: nombre.w
Pour connaitre son adresse mémoire (adresse mémoire de l'octet de poids faible) je place un @ devant le nom de la variable: soit @nombre


Ensuite à partir de son adresse, je peux lire ou écrire dans cette variable de deux manières. La première sont les instructions Peek (lecture) et poke (écriture) que l'on complète par une lettre suivant le type de la variable qui va déterminé le nombre d'octets à lire.

Comme nombre est déclaré de type word(.w), nous utiliserons donc, PeekW et PokeW

Code : Tout sélectionner

; Exemple pratique:
nombre.w=256
adresse=@nombre
debug peekW(@nombre)
Allons un peu plus loin. Le nombre.w est définit sur deux octets et à chaque octet correspond une adresse mémoire. Nous allons le vérifier. Pour lire chaque octet, nous utiliserons PeekB.

Code : Tout sélectionner

nombre.w=256
adresse1=@nombre
adresse2=@nombre+1
debug peekb(adresse1) ; le premier octet de la premiere adresse
;memoire de la variable est 0 comme expliqué plus haut
debug peekb(adresse2) ; le deuxième contient 1
De même pour écrire dans une variable avec un pointeur :

Code : Tout sélectionner

nombre.w=256
adresse=@nombre
debug peekW(adresse)
; je change la valeur de la variable nombre
pokeW(adresse,123)
debug peekW(adresse)
debug nombre
L'une des premières utilisations des pointeurs est son utilisation dans les procédures.

Un pointeur utilisé dans une procédure peut nous permettre de renvoyer plusieurs valeurs.

Nous connaissons déjà le passage de paramètres par valeur. Dans l'exemple qui suit, la procédure va copier les valeurs de nombre1 et nombre2 et les affecter à valeur1 et valeur2. La modification des variables valeur1 et valeur2 ne peut affecter en aucun cas les variables nombre1 et nombre2.

Code : Tout sélectionner

; Exemple de passage de paramètres par valeur
Procedure.l addition(valeur1.l, valeur2.l)
  valeur=valeur1+valeur2
  ProcedureReturn valeur
EndProcedure

nombre1.l=11
nombre2.l=22
Resultat=addition(nombre1, nombre2)
Debug Resultat ; resultat renvoie la valeur 33


Soit le code ci-dessus, imaginons que je souhaite pour deux valeurs obtenir non seulemment l'additon de ces deux valeurs mais aussi leur multiplication sans passer par des variables globales; nous allons devoir utiliser les pointeurs comme ceci:

Code : Tout sélectionner

;  Exemple de passage de paramètres par pointeur (ou adresse)
Procedure.l test(*valeur1.l, *valeur2.l)
  addition=PeekL(*valeur1)+PeekL(*valeur2)
  multiplication=PeekL(*valeur1)*PeekL(*valeur2)
  PokeL(*valeur1,addition)
  PokeL(*valeur2,multiplication)
  ProcedureReturn 1
EndProcedure

nombre1.l=11
nombre2.l=22
Resultat=test(@nombre1, @nombre2)
Debug nombre1 ; résultat de l'addition
Debug nombre2 ; résultat de la multiplication
Dans cet exemple, nous avons passé à la procédure non plus les variables elles-mêmes, mais leurs adresses respectives à la procédure. La procédure utilise les pointeurs représentés par *valeur1 et *valeur2 pour pointer vers ces adresses. Les variables nombre1 et nombre2 ont servis de variable d'entrée-sortie puisque dans un premier temps, elles ont fournis leurs valeurs à la procédure et dans un second temps, elles ont permis de récupérer les résultats de l'addition et de la multiplication. Contrairement au premier exemple, la modification du contenu des variables pointés par les pointeurs a modifié les variables nombre1 et nombre2.

-------------------------------------------------------------------------------------

Les pointeurs et les variables :

Exercice 1 :

Code : Tout sélectionner

; Un petit test pour se mettre en bouche
; Nous avons 3 pointeurs et une variable
; chaque pointeur contient l'adresse du pointeur suivant
; et le dernier pointeur contient l'adresse de la variable
; Comment faire pour lire la valeur de la variable
; à partir du premier pointeur, c'est à dire *pointeur1 ?
Ma_variable=123
*pointeur1:*pointeur2:*pointeur3
*pointeur1=@*pointeur2
*pointeur2=@*pointeur3
*pointeur3=@Ma_variable
Solution 1 (utilisation des PEEK) :

Code : Tout sélectionner

Ma_variable=123
*pointeur1:*pointeur2:*pointeur3
*pointeur1=@*pointeur2
*pointeur2=@*pointeur3
*pointeur3=@Ma_variable

Debug PeekL(PeekL(PeekL(*pointeur1)))
Solution 2 (Utilisation des pointeurs) :

Code : Tout sélectionner

Ma_variable=123
*pointeur1:*pointeur2:*pointeur3
*pointeur1=@*pointeur2
*pointeur2=@*pointeur3
*pointeur3=@Ma_variable

Structure test
  Valeur.l
EndStructure

*pointeur.test=*pointeur1

Debug *pointeur\Valeur

*pointeur.test=*pointeur\Valeur

Debug *pointeur\Valeur

*pointeur.test=*pointeur\Valeur

Debug *pointeur.test\Valeur
Explications :

Un pointeur fait référence à une variable (ou autre). Il se sert de l'adresse de cette variable pour créer cette référence. Un pointeur ne contient pas de données, mais pointe vers la variable, via l'adresse qu'il contient, dont il peut manipuler la ou les données.


Je crée un nouveau pointeur de type long *Pointeur.test

Code : Tout sélectionner

*Pointeur.test=*pointeur1
Ici le Pointeur pointe vers la même référence que *pointeur1. Donc *Pointeur et *pointeur1 font référence à la même adresse qui est celle de pointeur2. Un pointeur ne peut et ne doit contenir qu'une adresse qui sert de référence.

Code : Tout sélectionner

Debug *Pointeur\valeur
Permet de lire le contenu de la variable pointée. Comme le contenu est égal à l’adresse de *pointeur2 (@*pointeur2), ici on affiche la valeur de l’adresse de *pointeur2.

Je me sers maintenant du contenu de *pointeur2 (qui est l’adresse de *pointeur3 : @*pointeur3), et je lis le contenu de *pointeur3 qui est égal à Ma_variable :

Code : Tout sélectionner

*pointeur.test=*npointeur\Valeur
Debug *pointeur.test\Valeur
Exercice 2 :

Code : Tout sélectionner

; simple exercice avec les chaines 

chaine.s="Pure Casic is the Cest" 

; Changer la lettre C par un B 
; en utilisant aucune fonction STRING
Solution :

Code : Tout sélectionner

chaine.s="Pure Casic is the Cest" 

; Changer la lettre C par un B 
; en utilisant aucune fonction STRING 

Structure Liste 
  Valeur.b 
EndStructure 

*pointeur.Liste=@chaine 

*pointeur+5 ;position du "C" de Casis à partir du début de la chaine 
*pointeur\Valeur=66 ;valeur Ascii du B 
*pointeur+13 ;position du C de "Cest" à partir de la position précédente 
*pointeur\Valeur=66 ;valeur Ascii du B 

Debug chaine

En traitant le problème d'une façon générale, ça donne ça:

Code : Tout sélectionner

*Pointeur.BYTE=@chaine ; .BYTE est une structure pré-déclarée dans PB

For a=1 To Len(chaine) 
  If *Pointeur\b=$43 
    *Pointeur\b=$42 
  EndIf 
  *Pointeur=*Pointeur+1 
Next a 

Debug chaine
Exercice 3 :

Code : Tout sélectionner

; Autre exercice avec les chaines 

chaine.s="Pure Basic is the Best" 
chaine1.s=Space(Len(chaine)) 
; Il faut que: chaine1 = "tseB eht si cisaB eruP" 
; soit la chaine inversée de la première chaine 
; traité le problème d'une façon générale 
; aucune fonction STRING ne devra être utilisée 
; à part la fonction len()
Solution :

Code : Tout sélectionner

chaine.s="Pure Basic is the Best" 
chaine1.s=Space(Len(chaine)) 

Structure Liste 
  Valeur.b 
EndStructure 

*pointeur1.Liste=@chaine 
*pointeur2.Liste=@chaine1 

*pointeur1+Len(chaine)-1 

For i=1 To Len(chaine) 
  *pointeur2\Valeur=*pointeur1\Valeur 
  *pointeur2+1 
  *pointeur1-1 
Next 

Debug chaine1
-------------------------------------------------------------------------------------

Les pointeurs et les tableaux :

Les exemples exposés ci-dessus sur les variables, sont applicables aussi aux tableaux à (n) dimensions.

Exercice :

Créer une procedure affichant les valeur d'un tableau à 2 dimensions, en passant les pointeurs en paramètres :

Code : Tout sélectionner

#dimx=5
#dimy=5

Dim tab.l(#dimx,#dimy) ;tableau de type .long

For i=0 To 5
  For j=0 To 5
    tab(i,j)=i+j+1 ;affectation de valeurs quelconques au tableau
    Debug tab(i,j)
  Next
Next
Solution :

Code : Tout sélectionner

#dimx=5
#dimy=5

Dim tab.l(#dimx,#dimy) ;tableau de type .long

For i=0 To 5
  For j=0 To 5
    tab(i,j)=i+j+1 ;affectation de valeurs quelconques au tableau
    Debug tab(i,j)
  Next
Next

Structure Liste
val.l
EndStructure

Procedure affich(*pointeur.Liste,nb)
  For i=1 To nb
    Debug *pointeur\val
    *pointeur+4 ;saut de 4 octets (valeur d'un .LONG)
  Next i
EndProcedure

Debug "------------------------------"

affich(@tab(),(#dimx+1)*(#dimy+1)) ;@tab() donne l'adresse du premier élément du tableau (tab(0,0))
Les pointeurs et les liste chainées :

Ceci n'est pas un tutorial, mais plutôt un éclaircissement sur la structure en mémoire d'une liste chainée. Lorsque l'on définit une liste chainées comme ceci :

Code : Tout sélectionner

structure Element
valeur.l
endstructure

NewList test.Element() 
Pure Basic modifie la structure en interne comme ceci:

Code : Tout sélectionner

structure Element
*Next.Element
*Previous.Element
valeur.l
endstructure 
Petite démonstration :

Code : Tout sélectionner

Structure Liste
  Valeur.l
EndStructure

NewList Test.Liste()

AddElement(Test())
Test()\Valeur=12

AddElement(Test())
Test()\Valeur=34

AddElement(Test())
Test()\Valeur=56

AddElement(Test())
Test()\Valeur=78

FirstElement(Test())
Debug Str(@Test()\Valeur) +"=adresse du 1er élément"

NextElement(Test())
Debug Str(@Test()\Valeur) +"=adresse du 2ème élément"

NextElement(Test())
Debug Str(@Test()\Valeur) +"=adresse du 3ème élément"

NextElement(Test())
Debug Str(@Test()\Valeur) +"=adresse du 4ème élément"

Debug "----------------------------"

FirstElement(Test())
adresse=@Test()\Valeur
Debug Str(PeekL(adresse-4)) +"=adresse élément precedent"
Debug Str(PeekL(adresse-8)) +"=adresse élément suivant"
Debug Test()\Valeur

Debug "----------------------------"

NextElement(Test())
adresse=@Test()\Valeur
Debug Str(PeekL(adresse-4)) +"=adresse élément precedent"
Debug Str(PeekL(adresse-8)) +"=adresse élément suivant"
Debug Test()\Valeur

Debug "----------------------------"

NextElement(Test())
adresse=@Test()\Valeur
Debug Str(PeekL(adresse-4)) +"=adresse élément precedent"
Debug Str(PeekL(adresse-8)) +"=adresse élément suivant"
Debug Test()\Valeur

Debug "----------------------------"

NextElement(Test())
adresse=@Test()\Valeur
Debug Str(PeekL(adresse-4)) +"=adresse élément precedent"
Debug Str(PeekL(adresse-8)) +"=adresse élément suivant"
Debug Test()\Valeur
Graphiquement nous pourrions la représenter comme ceci :

Image

Voilà, j'espère que ça va éclaircir pas mal de lanternes... Enfin, moi ça m'a permis d'y voir un peu plus clair...

Merci à Nico et Comtois.

PS: les corrections et les suggestions pour améliorer ce tuto sont les bienvenus. :D
Dernière modification par Torp le jeu. 10/sept./2009 21:48, modifié 4 fois.
comtois
Messages : 5186
Inscription : mer. 21/janv./2004 17:48
Contact :

Message par comtois »

il te reste à attaquer l'arbre lexicographique ? :)

tu devrais envoyer ce tut à Crystal noir qu'il le mette en bonne place sur son site :)
Le Soldat Inconnu
Messages : 4312
Inscription : mer. 28/janv./2004 20:58
Localisation : Clermont ferrand OU Olsztyn
Contact :

Message par Le Soldat Inconnu »

Oui, on va faire ça :D
Je ne suis pas à moitié Polonais mais ma moitié est polonaise ... Vous avez suivi ?

[Intel quad core Q9400 2.66mhz, ATI 4870, 4Go Ram, XP (x86) / 7 (x64)]
Torp
Messages : 360
Inscription : lun. 22/nov./2004 13:05

Message par Torp »

Ce serait bien qu'il soit "épinglé" au début du forum, avec les autres Tuto.
Torp
Messages : 360
Inscription : lun. 22/nov./2004 13:05

Message par Torp »

Un modo est demandé à l'acceuil... :)
Avatar de l’utilisateur
Crystal Noir
Messages : 892
Inscription : mar. 27/janv./2004 10:07

Message par Crystal Noir »

excellent travail.

Avec ton autorisation, je le mettrai sur 2Dev.

Ce post mérite d'être placé en haut de liste :)
Torp
Messages : 360
Inscription : lun. 22/nov./2004 13:05

Message par Torp »

C'est fait pour ... :)
novicenpure
Messages : 34
Inscription : jeu. 20/janv./2005 21:30

Message par novicenpure »

Franchement, très bon travail, moi qui voulais comprendre simplement les pointeurs. En plus ça tombe bien, je commence un programme d'optimisation de paramètres avec des matrices et des vecteurs....

Merci aux programmeurs confirmés de se mettre au niveau des petits. C'est vraiment sympa.

A +
Avec suffisamment de paires d'yeux, tous les bogues feront surface (Linus Torvalds).
bombseb
Messages : 445
Inscription : jeu. 25/août/2005 22:59
Localisation : 974
Contact :

Message par bombseb »

Moi j'ai quelques chtite questions svp...si vous pouviez m'expliquer ca serais super cool...

Question 1

Solution 2 (Utilisation des pointeurs) :

Code:

Ma_variable=123
*pointeur1:*pointeur2:*pointeur3
*pointeur1=@*pointeur2
*pointeur2=@*pointeur3
*pointeur3=@Ma_variable

Structure test
Valeur.l
EndStructure

*pointeur.test=*pointeur1

Debug *pointeur\Valeur

*pointeur.test=*pointeur\Valeur

Debug *pointeur\Valeur

*pointeur.test=*pointeur\Valeur

Debug *pointeur.test\Valeur


dans cet exemple je ne comprend pas à quoi sert cette ligne :
*pointeur1:*pointeur2:*pointeur3
si c'est un déclaration de variables de types pointeurs j'aurais plutot vu un truc de ce style
pointer pointeur1
pointer pointeur2
etc...

et puis ca sert à quoi de les déclarer vu qu'on est pas obligés de déclarer les variables en basic ?


Question 2
*pointeur.test=*pointeur1


je ne comprend pas la partie à gauche du "=" pourquoi on ne fait pas un truc comme ca :

adresse = *pointeur1


Question 3
Structure test
Valeur.l
EndStructure
pourquoi on passe par une structure ???? je trouve ca vraiment bizarre !
on aurais pas pu passer par une variable toute bête pour stocker l'adresse ????

J'imagine que ces questions vont vous paraitre vraiment bête... désolé je suis encore débutant et j'espere que vous allez pouvoir me reconcilier avec les pointeurs...
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

Question 1

c'est effectivement une sorte de déclaration qui n'a rien d'indispensable ni de très élégant dans l'exemple. La déclaration dévient obligatoire quand il s'agit de structures. De même lorsqu'on veut spécifier le type de données...

Question 2

en général c'est plutot le pointeur qui recoi l'adresse et pas l'inverse ;)

Question 3

le purebasic n'exploite réellement les concepts de pointeurs que par l'utilisation de structures. sa syntaxe est très simple et ne permet pas autant de liberté qu'en C... Si tu vas voir dans les trucs et astuces on a réussi à "émuler" le principe de pointeur tableau du C en PB 8)

Dri ;)

PS. les questions ne sont pas bêtes si elles sont pertinentes ;)
bombseb
Messages : 445
Inscription : jeu. 25/août/2005 22:59
Localisation : 974
Contact :

Message par bombseb »

merci beaucoup pour tes réponses, je vais potasser tout ca...
wolfjeremy
Messages : 1202
Inscription : sam. 31/déc./2005 23:52

Message par wolfjeremy »

C'est important les pointeur ? sa sert a quoi exactement ? bien expliquer, gros debutant inside lol
Frenchy Pilou
Messages : 2194
Inscription : jeu. 27/janv./2005 19:07

Message par Frenchy Pilou »

Cela sert juste à avoir plus de contrôle sur toutes ces petites bêtes que sont les variables et autres joyeusetés.
Savoir où elles se trouvent, comment les attraper, les transférer :)
Mais ce n'est pas indispensable dans un usage courrant!
C'est pour ceux qui veulent tripatouiller sous le capot :lol:

Néanmoins très beau tut! 8)
Est beau ce qui plaît sans concept :)
Speedy Galerie
wolfjeremy
Messages : 1202
Inscription : sam. 31/déc./2005 23:52

Message par wolfjeremy »

Ok merci Frenchy Pilou je ne vai pas m'en occupé donc... pour le debut en tout cas.
wolfjeremy
Messages : 1202
Inscription : sam. 31/déc./2005 23:52

Message par wolfjeremy »

Frenchy Pilou a écrit : C'est pour ceux qui veulent tripatouiller sous le capot :lol:
Salut,

Voila maintenant que je connais mieu le PureBasic et l'API Windows, je souhaite me lancer dans les pointeurs (il était temps hein ? lol)
Comme tu le dit je commence a tripatouiller sous le capot.

C'était aussi pour dire que les images du tuto ne sont plus visible, il faut remplacer les liens.

EIDT : c'est bon les images remarche :?
Répondre