
Un pointeur, c'est l'adresse d'une valeur en mémoire plutôt que la valeur elle-même. Ce guide vous apprend à les utiliser en Go : lire une valeur à travers un pointeur, modifier une variable depuis une fonction, et éviter la panique du pointeur nil. C'est le concept qui décide si une fonction copie vos données ou les modifie sur place. Public visé : vous maîtrisez déjà les fonctions et les structures. Exemples testés sur Go 1.26.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Déclarer un pointeur avec
&et lire sa valeur avec*. - Choisir entre passage par valeur et par pointeur.
- Éviter les paniques sur un pointeur
nil. - Comprendre pourquoi Go interdit l'arithmétique de pointeurs.
& et * : adresse et déréférencement
Section intitulée « & et * : adresse et déréférencement »Deux opérateurs suffisent. L'esperluette & donne l'adresse d'une variable (on obtient un pointeur). L'étoile * fait l'inverse : elle déréférence le pointeur pour accéder à la valeur pointée.
x := 42p := &x // p est un *int : l'adresse de xfmt.Println(*p) // 42 : la valeur à cette adresse*p = 100 // on écrit à travers le pointeurfmt.Println(x) // 100 : x a changéModifier *p modifie x, car les deux désignent la même case mémoire. Un pointeur sur un int a le type *int, sur une struct Compte le type *Compte, et ainsi de suite.
Passer par valeur ou par pointeur
Section intitulée « Passer par valeur ou par pointeur »C'est l'usage principal des pointeurs. Par défaut, Go passe les arguments par valeur : la fonction reçoit une copie. Pour qu'une fonction modifie l'original, on lui passe un pointeur.
type Compte struct { Solde int}
func crediterValeur(c Compte, montant int) { c.Solde += montant } // sur une copiefunc crediterPointeur(c *Compte, montant int) { c.Solde += montant } // sur l'original
func main() { c := Compte{Solde: 50} crediterValeur(c, 10) fmt.Println(c.Solde) // 50 : inchangé, on a modifié une copie crediterPointeur(&c, 10) fmt.Println(c.Solde) // 60 : l'original est modifié}crediterValeur travaille sur une copie qui disparaît à la fin de la fonction : l'original reste à 50. crediterPointeur reçoit l'adresse et modifie bien le compte. Le pointeur évite aussi de copier une grosse structure à chaque appel, ce qui compte pour la performance.
- Quand la fonction doit modifier son argument.
- Quand la structure est grosse et qu'une copie coûterait cher.
- Pour un récepteur de méthode qui mute l'objet (voir les méthodes).
À l'inverse, pour de petites valeurs en lecture seule (un int, un petit struct), le passage par valeur est plus simple et souvent aussi rapide. Ne mettez pas des pointeurs partout par réflexe.
Le pointeur nil et les paniques
Section intitulée « Le pointeur nil et les paniques »Un pointeur qui ne pointe vers rien vaut nil. Le déréférencer fait paniquer le programme (nil pointer dereference). C'est l'erreur la plus courante avec les pointeurs.
var np *Compte // déclaré mais pointe vers rien : nilif np == nil { fmt.Println("np est nil, on ne le déréférence pas")}np = &Compte{} // maintenant il pointe vers un Compte réelnp.Solde = 5 // OKfmt.Println(np.Solde)Le réflexe : vérifier != nil avant de déréférencer un pointeur dont l'origine est incertaine (retour de fonction, champ optionnel). Une fois initialisé avec &Compte{} ou new(Compte), il est sûr à utiliser.
Pas d'arithmétique de pointeurs
Section intitulée « Pas d'arithmétique de pointeurs »Contrairement au C, Go interdit l'arithmétique de pointeurs : on ne peut pas faire p + 1 pour se déplacer en mémoire. C'est un choix de sécurité qui élimine toute une classe de bugs (débordements, corruption mémoire). Pour parcourir une suite d'éléments, on utilise un slice et range, pas un pointeur qu'on incrémente.
Exercice : échanger deux variables
Section intitulée « Exercice : échanger deux variables »Un classique des pointeurs. Cherchez la solution avant d'ouvrir l'onglet Réponse.
Écrivez une fonction echanger qui inverse les valeurs de deux variables int, de sorte que l'appelant voie le changement.
- Sa signature doit accepter deux pointeurs :
func echanger(a, b *int). - Après
echanger(&x, &y),xetydoivent avoir échangé leurs valeurs.
Indice : Go permet l'affectation multiple *a, *b = *b, *a en une ligne.
En passant des pointeurs, la fonction agit sur les variables d'origine. L'affectation multiple échange les deux valeurs sans variable temporaire.
package main
import "fmt"
func echanger(a, b *int) { *a, *b = *b, *a}
func main() { x, y := 1, 2 echanger(&x, &y) fmt.Println(x, y) // 2 1}Résultat :
2 1Si echanger prenait des int par valeur (func echanger(a, b int)), elle n'échangerait que ses copies locales et x/y resteraient inchangés. C'est tout l'intérêt du pointeur : agir sur l'original.
À retenir
Section intitulée « À retenir »&xdonne l'adresse dex(un pointeur) ;*pdéréférence pour lire ou écrire la valeur pointée.- Go passe les arguments par valeur (copie) ; un pointeur permet à une fonction de modifier l'original.
- Utilisez un pointeur pour muter un argument ou éviter de copier une grosse structure, pas par réflexe.
- Un pointeur
nildéréférencé fait paniquer : vérifiez!= nilen cas de doute. - Go interdit l'arithmétique de pointeurs : pour parcourir des données, on utilise un slice, pas un pointeur incrémenté.