
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.

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)
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
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
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
-------------------------------------------------------------------------------------
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
Code : Tout sélectionner
Ma_variable=123
*pointeur1:*pointeur2:*pointeur3
*pointeur1=@*pointeur2
*pointeur2=@*pointeur3
*pointeur3=@Ma_variable
Debug PeekL(PeekL(PeekL(*pointeur1)))
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
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
Code : Tout sélectionner
Debug *Pointeur\valeur
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
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
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
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
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()
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
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))
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()
Code : Tout sélectionner
structure Element
*Next.Element
*Previous.Element
valeur.l
endstructure
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

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.
