Les pointeurs
Un des fondements du langage C est la manipulation directe de la mémoire. C’est une vraie particularité du C, car tous les langages de haut niveau comme Java ou Python mettent en oeuvre des mécanismes automatiques de réservation, protection et libération de la mémoire utilisée.
La manipulation de la mémoire va nécessiter d’accéder à l’adresse des variables. On se servira pour cela d’un pointeur, une variable particulière qui ne contient pas de valeur mais l’adresse mémoire d’une autre variable.
L’utilisation des pointeurs est incontournable, elle facilite la programmation dans certains cas, voire est indispensable dans d’autres.
Le principe
L’idée générale est de ne plus manipuler seulement les valeurs des variables, mais également leur adresse mémoire.
On considère le code source suivant, dans lequel deux variables sont déclarées en mémoire :
1// pointers.c
2
3#include <stdio.h>
4
5int main()
6{
7 int b = 4;
8 int* a = &b;
9 return 0;
10}
Dans ce programme :
b
est une variable de typeint
(entier) dont la valeur est initialisée à4
;a
est une variable de typeint*
(pointeur vers un entier) et dont la valeur est initialisée à l’adresse deb
.
La variable a
contient une valeur particulière qui n’est ni un int
, ni un double
, ni un char
. Il s’agit de l’adresse d’une autre variable.
Modifions un peu le code pour afficher les valeurs et les adresses de chacune des deux variables a
et b
:
1// pointers.c
2
3#include <stdio.h>
4
5int main()
6{
7 int b = 4;
8 int* a = &b;
9
10 printf("b = %d\n", b); // valeur de b
11 printf("&b = %p\n", &b); // adresse de b
12 printf("a = %p\n", a); // valeur de a
13 printf("*a = %d\n", *a); // valeur pointée par a
14 printf("&a = %p\n", &a); // adresse de a
15
16 return 0;
17}
Observer attentivement ce code, et en particulier
l’emplacement réservé
%p
utilisé pour afficher un pointeur (l’adresse d’une variable) (ligne 11) ;l’opérateur
&
permettant d’accéder à l’adresse d’une variable (ligne 11) ;l’opérateur d’indirection
*
utilisé pour accéder au contenu d’un pointeur (ligne 13).
Note
l’opérateur
&
s’applique à toute variable ;l’opérateur d’indirection
*
n’est valide que si la variable à laquelle il s’applique est un pointeur ;lorsqu’ils sont chainés, les opérateurs
&
et*
s’évaluent de droite à gauche.
Ce code produit l’affichage suivant (les adresses mémoires seront différentes sur votre machine)
b = 4
&b = 0x7ffd1191ca00
a = 0x7ffd1191ca00
*a = 4
&a = 0x7ffd1191c7ac
ce qui correspond à la figure ci dessous.
En résumé :
b
est une variable de typeint
(entier) située à l’adresse0x7ffd1191ca00
et dont la valeur est4
;a
est une variable de typeint*
(pointeur vers un entier) située à l’adresse0x7ffd1191c7ac
et dont la valeur est0x7ffd1191ca00
(l’adresse deb
). Le contenu de la variable pointée para
est4
.
Dans ce qui suit, b
est un int
et a
un pointeur vers b
.
01 - L’opérateur d’indirection, utilisé pour récupérer la valeur d’une variable dont on connait le pointeur est
02 - L’opérateur permettant d’obtenir l’adresse d’une variable est
03 - l’expression
&a
est valide04 - l’expression
&b
est valide05 - l’expression
*a
est valide06 - l’expression
*b
est valide07 - l’expression
&*a
est valide08 - l’expression
&*a
fournit l’adresse dea
09 - l’expression
&*a
fournit l’adresse deb
10 - l’expression
&*a
fournit la valeur dea
11 - l’expression
&*a
fournit la valeur deb
12 - l’expression
&*b
est valide13 - l’expression
&*b
fournit l’adresse dea
14 - l’expression
&*b
fournit l’adresse deb
15 - l’expression
&*b
fournit la valeur dea
16 - l’expression
&*b
fournit la valeur deb
17 - l’expression
*&a
est valide18 - l’expression
*&a
fournit l’adresse dea
19 - l’expression
*&a
fournit l’adresse deb
20 - l’expression
*&a
fournit la valeur dea
21 - l’expression
*&a
fournit la valeur deb
22 - l’expression
*&b
est valide23 - l’expression
*&b
fournit l’adresse dea
24 - l’expression
*&b
fournit l’adresse deb
25 - l’expression
*&b
fournit la valeur dea
26 - l’expression
*&b
fournit la valeur deb
La déclaration
Un pointeur est une variable dont la valeur est l’adresse mémoire d’une autre variable. Comme toute variable dans un programme C elle doit être déclarée avant utilisation.
Pour illustrer ça, commentons la ligne 7 du code ci dessus, ce qui a pour effet de ne pas déclarer la variable b
. Le compilateur produit un message du type:
pointers.c: In function ‘main’:
pointers.c:7:15: error: ‘b’ undeclared (first use in this function)
7 | int* a = &b;
| ^
pointers.c:7:15: note: each undeclared identifier is reported only once for each function it appears in
Analyser toutes les informations fournies par ce message d’erreur.
L’erreur de compilation concerne le fichier
L’erreur de compilation concerne la fonction
L’erreur de compilation se situe à la ligne
On peut localiser la position de l’erreur de compilation sur la ligne
L’erreur de compilation est due à l’absence d’initialisation
L’erreur de compilation est due à l’absence de déclaration
La forme générale de déclaration des pointeurs est la suivante:
type *varname;
L’astéristique identifie varname
comme un pointeur vers une variable de type type
. Pour les types courants :
int *i; /* pointeur vers un int */
double *d; /* pointeur vers un double */
char *c /* pointeur vers un character */
Note
Il y a deux écritures possibles pour la déclaration d’un pointeur. Les deux sont valides, équivalentes et expriment deux façons de voir les choses. Selon le cas, il sera plus signifiant de considérer l’une ou l’autre :
int *i;
exprime le fait que le contenu dei
est unint
;int* i;
exprime le fait quei
est un pointeur vers unint
.