PureBasic     Discussion     Modifier     Historique     Forums     Salon IRC

PureBasic:Pointeurs

Un article de Games Creators Network.

Sommaire

[modifier] Introduction

Vous venez de lire la doc sur les pointeurs et accès mémoire et ce n'est toujours pas clair ? espérons que cet article vous aidera un peu plus .

[modifier] Adresse

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éfini sur deux octets mais son adresse mémoire est définie 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.

PB_Memoire01.jpg

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.

[modifier] 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:

@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() et Poke() 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()

; 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.

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 :

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.

; 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'addition de ces deux valeurs mais aussi leur multiplication sans passer par des variables globales; nous allons devoir utiliser les pointeurs comme ceci:

;  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ées par les pointeurs a modifié les variables nombre1 et nombre2.

[modifier] Les pointeurs et les variables

Exercice 1 :

; 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) :

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

Debug PeekL(PeekL(PeekL(*pointeur1)))

Solution 2 (Utilisation des pointeurs) :

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

*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.

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 :

*pointeur.test=*npointeur\Valeur
Debug *pointeur.test\Valeur

Exercice 2 :

; 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 1 :

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

Solution 2 (sans structure - positionnement relatif) :

chaine.s="Pure Casic is the Cest"

; Changer la lettre C par un B
; en utilisant aucune fonction STRING
*pointeur=@chaine
 *pointeur+5
 PokeB(*pointeur,66)
 *pointeur+13
 PokeB(*pointeur,66)

Debug chaine

Solution 3 (sans structure - positionnement absolu) :

chaine.s="Pure Casic is the Cest"

; Changer la lettre C par un B
; en utilisant aucune fonction STRING
*pointeur=@chaine
PokeB(*pointeur+5,66)
PokeB(*pointeur+18,66)

Debug chaine

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

*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

ou ça :

*Pointeur=@chaine ; Un pointeur sur la chaîne

For a=1 To Len(chaine)
  If PeekB(*Pointeur)=$43
    PokeB(*Pointeur,$42)
  EndIf
  *Pointeur+1
Next a

Debug chaine

Exercice 3 :

; 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 1 :

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

Solution 2 :

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

*pchaine  = @chaine
*pchaine1 = @chaine1
taille.l  = Len(chaine)-1

For i.l = 0 To taille
  PokeB(*pchaine1 + taille - i , PeekB(*pchaine + i))
Next

Debug chaine
Debug chaine1

[modifier] 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 :

#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 :

#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))

[modifier] 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 :

structure Element
valeur.l
endstructure

NewList test.Element()

Pure Basic modifie la structure en interne comme ceci:

structure Element
*Next.Element
*Previous.Element
valeur.l
endstructure

Petite démonstration :

<pre>
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:PB_Memoire02.jpg

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...

 

Rechercher
Installer l'extension de recherche Plus d'informations

 

Comprendre
Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. - Benjamin Franklin

 

Partager
La connaissance est la seule chose qui s'accroit lorsqu'on la partage. - Sacha Boudjema

 

Créer
L'imagination est plus importante que la connaissance. - Albert Einstein

 

 

Le wiki en images Le wiki en images Image du mois: «Snowball: un prototype de jeu développé avec NeL.