Pointeurs C++ en une minute
Comme tout le monde, j'ai longtemps lutté pour comprendre comment fonctionnent les pointeurs en C++.
Puis un jour, j'ai lu une simple phrase qui m'a ouvert les neurones et permis, enfin, de saisir quand et pourquoi utiliser &
et *
pour manipuler les pointeurs.
Ceci n'est pas un tuto
Cher lecteur, ceci n'est pas un tutoriel sur les pointeurs, encore moins une explication de pourquoi ils existent et pourquoi les utiliser : c'est simplement un partage de cette explication succinte qui m'a remis les idées en place.
Variables et adresses
On stocke une valeur, 33, dans une variable nommée “num" :
int num;
num = 33;
C'est simple : num
est une boîte, de type "int” (integer, un nombre entier), qui contient la valeur 33. Cette boîte est stockée en mémoire. Quelque part dans la mémoire. Où ? C'est l'ordinateur qui décide.
On peut lui demander :
int* address = #
Ici, address
est une nouvelle variable, qui contient l'adresse de la variable num
, et non pas la valeur de num
.
Pourquoi ?
Parce que &num
retourne un pointeur, un lien, alors que num
retourne une valeur.
Et ce pointeur se note avec *
, donc on déclare int*
(un pointeur vers un integer) et on lui assigne &num
, qui pointe vers num
.
On déclare le pointeur avec type* name
, on récupère l'adresse de thing
avec &thing
, et on la stocke dans name
.
Si on laisse le compilateur inférer le type, l'étoile disparaît mais on a exactement le même résultat :
auto address = #
Maintenant, la principale source de confusion vient du fait que cet opérateur étoile *
est également ce qu'on utilise pour déréférencer un pointeur, c'est-à-dire pour retrouver la valeur vers laquelle pointe un pointeur.
En continuant notre exemple :
int result = *address;
Ici result
est 33, parce que l'on a demandé non pas l'adresse elle-même, mais ce vers quoi elle pointe.
Attention donc à ne pas confondre l'usage de l'opérateur *
s'il est placé avant ou après le =
de l'assignation !
La révélation
Après ce petit rappel, voici la fameuse phrase qui m'a bien aidé.
Dans le contexte de la citation, a
est une variable qui contient un pointeur.
Because
&a
means “the address ofa
” and*a
means “the address thata
is pointing to”.
Traduction :
Parce que
&a
signifie “l'adresse dea
” et*a
signifie “l'adresse vers laquellea
pointe”.
Et oui : un pointeur pointe vers une adresse, mais ce pointeur possède lui-même une adresse, et peut-être que le pointeur pointe vers un autre pointeur !
Ce n'est pas parce que l'on déréférence un pointeur (autrement dit, que l'on récupère la valeur vers laquelle il pointe) que ce que l'on obtient est une valeur - cela peut également être un pointeur.
L'exemple
Et maintenant un petit exemple qui relie les deux concepts exposés ci-dessus.
int num;
num = 33;
auto point = #
auto val = *point;
Que se passe-t-il ici ?
int num
déclare une variable de type nombre entier
num = 33
assigne la valeur 33
à la variable nommée num
auto point = &num
ici la variable point
contient l'ADRESSE de la variable num
auto val = *point
et enfin, la variable val
contient 33
car on déréférence point
et donc on obtient ce vers quoi il pointe
Donc point
est un pointeur (une variable qui contient l'adresse d'une variable). En faisant *point
on obtient num
parce que point
pointe vers l'adresse de num
.
Mais nous aurions pu également faire auto wow = &point
qui nous donnerait alors l'adresse du pointeur (pas le pointeur lui-même !). Cela entraînera la création de deux niveaux d'indirection entre wow
et num
. En effet wow
pointe vers point
qui pointe vers num
qui contient 33
.
Ouf !
Voici l'exemple complet :
int main()
{
int num;
num = 33;
auto point = #
auto val = *point;
auto paddr = &point;
auto pval = *paddr;
auto unval = *pval;
std::cout << num << std::endl;
std::cout << point << std::endl;
std::cout << val << std::endl;
std::cout << paddr << std::endl;
std::cout << pval << std::endl;
std::cout << unval << std::endl;
}
Résultat :
33 // num
0x77787272fdf4 // point, l'addresse de num
33 // val, ce vers quoi pointe point, en fait num
0x77787272fdf8 // paddr, l'addresse du pointeur point lui-même
0x77787272fdf4 // pval, le contenu de paddr, donc l'addresse de num
33 // le déréférencement de pval, donc num
Voilà, ce n'est absolument qu'un aperçu de la surface de ce vaste et complexe sujet que sont les pointeurs, mais ça offre un angle de compréhension qui m'a fait faire un énorme progrès, et j'espère que cela vous sera également utile.