Pointeurs et accès mémoire

Pointeurs

L'utilisation des pointeurs est possible en plaçant une étoile '*' devant le nom d'une variable d'une liste ou d'une map.
Un pointeur est un emplacement en mémoire qui stocke une adresse mémoire et qui est généralement associé à une structure.

Exemple

  *MonEcran.Ecran = OpenScreen(0,320,200,8,0)
  mouseX = *MonEcran\SourisX ; La structure Ecran devant contenir un champ SourisX
Il existe seulement trois méthodes valides pour fixer la valeur d'un pointeur:
- Obtenir le résultat par une fonction (voir l'exemple ci-dessous)
- Copier la valeur d'un autre pointeur
- Trouver l'adresse d'une variable, procédure ou label (voir ci-dessous)

Note : A l'inverse du C/C++, en PureBasic l' '*' fait partie intégrante du nom de la variable. Aussi ptr et *ptr sont deux variables bien distinctes.
ptr est une variable (régulière) contenant une valeur, *ptr est une autre variable de type pointeur contenant une adresse.

Pointeurs et taille mémoire

Comme un pointeur reçoit uniquement une adresse mémoire comme valeur, sa taille en mémoire sera celle qui permettra de représenter une adresse du processeur :
- Sur un processeur 32-bit, les adresses sont représentées sur 32-bit, par conséquent un pointeur prendra 32-bit en mémoire (soit 4 octets comme une variable de type ‘long').
- Sur les processeurs 64-bit, les adresses sont représentées sur 64-bit, ce qui implique qu'un pointeur prendra 64-bit en mémoire (soit 8 octets comme une variable de type ‘quad').

C'est pour cette raison qu'un pointeur est une variable dite de type pointeur car son encombrement en mémoire sera lié à la capacité d'adressage mémoire du processeur.
Il en découle qu'affecter un type natif à un pointeur (*Pointeur.l, *Pointeur.b…) n'a aucun sens puisque l'encombrement mémoire d'un pointeur est imposé par celui d'une adresse et non par celui d'un type.

Note :
- A chaque fois qu'une adresse mémoire doit être stockée dans une variable, il faudrait le faire par l'intermédiaire d'un pointeur. Ceci garanti que l'adresse sera correctement représentée lors de la compilation du code que ce soit par un processeur 32-bit comme par un processeur 64-bit par exemple.


Pointeurs et structures

Attacher une structure à un pointeur (par exemple *MonPoint.Point) permet d'accéder au contenu mémoire de chaque membre de la structure avec le caractère \ .

Exemple: Pointeurs et variables structurées

  Define Point1.Point, Point2.Point
  *PointCourant.Point = @Point1 ; Déclare le pointeur, l'associe à une structure et l'initialise avec l'adresse de Point1
  *PointCourant\x = 10          ; Assigne la valeur 10 à Point1\x
  *PointCourant.Point = @Point2 ; Récupère l'adresse de Point2
  *PointCourant\x = 20          ; Assigne la valeur 20 à Point2\x
  Debug Point1\x
  Debug Point2\x
Pointeurs et tableaux

Attacher un tableau à un pointeur permet d'accéder au contenu mémoire de chaque cellules à travers son adresse.

Exemple: Pointeurs et tableaux

  Define Point1.Point, Point2.Point ; 2 Variables de type 'point' (type prédéfini dans PureBasic)
  Dim *Points.Point(1) ; Un tableau de 2 pointeurs et le tableau est de type 'Point'
  *Points(0) = @Point1 ; Le premier pointeur contient l'adresse de la variable Point1
  *Points(1) = @Point2 ; Le second pointeur contient l'adresse de la variable Point2

  *Points(0)\x = 10 ; Modification de la variable Point1 à travers le pointeur
  *Points(1)\x = 20 ; Idem avec Point2
  
  Debug Point1\x
  Debug Point2\x
Les pointeurs permettent donc de se déplacer, de lire et d'écrire facilement en mémoire. De plus ils permettent aux programmeurs d'accéder à de grandes quantités de données sans coût supplémentaire suite à une duplication de ces données. Copier un pointeur est beaucoup plus rapide.

Les pointeurs sont également disponibles dans les structures, pour plus d'informations, consultez le chapitre sur les structures.

Pointeurs et chaînes de caractères

Toutes les variables ont une taille fixe en mémoire (2 octets pour un Word, 4 octets pour un Long, etc) hormis les chaînes de caractères dont la longueur peut changer, ce qui fait qu'elles sont gérées différemment.
Ainsi, les champs d'une structure faisant référence à une chaîne de caractères stockent l'adresse mémoire où réside la chaîne de caractères et non la chaîne elle-même: ce sont des pointeurs vers des chaînes de caractères.

Exemple

  Texte$ = "Bonjour"
  *Texte = @Texte$ ;*Texte a pour valeur l'adresse où réside la chaîne de caractères en mémoire
  *Pointeur.String = @*Texte ; *Pointeur pointe sur *Texte
  Debug *Pointeur\s ; Lit la chaîne de caractères qui réside à l'adresse écrite en *Pointeur (c-a-d @Texte$)
Arithmétiques des pointeurs

Il est possible d'effectuer des opérations arithmétiques sur les pointeurs en s'aidant de la commande SizeOf().

Exemple

  Dim Tableau.Point(1) ; tableau de points 

  *Pointeur.Point = @Tableau() ; Récupère l'adresse du tableau
  *Pointeur\x = 10 ; Modifie l'élément 0 du tableau 
  *Pointeur\y = 15 
  
  *Pointeur + SizeOf(Point) ; Pointe sur l'élément suivant 
  
  *Pointeur\x = 7 ; Modifie l'élément 1 du tableau 
  *Pointeur\y = 9

  ;Affiche le résultat
  For i = 0 To 1
    Debug Tableau(i)\x
    Debug Tableau(i)\y
  Next i

Adresses des variables : '@'

Pour obtenir l'adresse d'une variable dans votre code, utilisez le symbole @. La raison la plus fréquente d'utiliser ce système est le transfert d'une variable de type structure à une procédure. Il faut passer un pointeur à la procédure car il est impossible de passer directement la structure comme argument.

Exemple

  Structure astruct
    a.w
    b.l
    c.w
  EndStructure
  
  Procedure SetB(*monpointeur.astruct)
    *monpointeur\b = 69
  EndProcedure
  
  Define.astruct mavariable
  mavariable\b = 0
  SetB( @mavariable )
  Debug mavariable\b

Adresses des chaînes littérales

Pour obtenir l'adresse d'une chaîne de caractères littérales, utilisez le symbole @ devant elle. Les chaînes constantes sont aussi supportées.

Exemple

  *Chaine = @"Test"
  Debug PeekC(*Chaine) ; Affiche 84, qui est la valeur de la lettre T majuscule 'T'

Adresses des procédures : '@'

En principe seuls les programmeurs avancés ont à connaître l'adresse d'une procédure. La raison la plus fréquente est de devoir négocier des échanges de bas niveau avec le système d'exploitation. Certains systèmes autorisent la mise en place de callbacks ou points d'ancrage (hooks) permettant au système d'exploitation de dialoguer avec le programme en étendant ainsi les capacités du système d'exploitation. L'adresse d'une procédure est accessible d'une manière similaire à une variable.

Exemple

  Procedure WindowCB(WindowID.i, Message.i, wParam.i, lParam.i)
    ; C'est ici que le traitement de votre callback sera effectué
  EndProcedure
  
  ; Un callback spécifique pour Windows permet de traiter les évènements sur les fenêtres
  SetWindowCallback( @WindowCB() )

Adresses des labels : '?'

Il peut également être utile de connaître l'adresse d'un label dans votre programme. Cela peut être le cas pour accéder au code ou aux données placées à cet endroit ou toute autre bonne raison qui peut vous venir à l'esprit. Pour trouver l'adresse d'un label dans votre programme, placez un '?' devant le nom du label.

Exemple

  Debug "Taille du fichier de données = " + Str(?endofmydata - ?mydata)
  
  DataSection
    mydata:
      IncludeBinary "somefile.bin"
    endofmydata: