VII. Introduction aux pointeurs▲
Toutes les variables et fonctions ont une adresse (emplacement de mémoire) qui leur est associée. C'est là qu'elles résident. Pour les données plus grandes qu'un octet, comme les tableaux, l'adresse est celle du premier élément du bloc de données. L'adresse de toute variable (à l'exception des variables définies avec la classe de stockage register) peut être trouvée avec l'opérateur &. Selon le système d'exploitation, l'adresse sera généralement contenue dans 2, 4 ou 8 octets. C'est-à-dire que toutes les adresses possibles du système peuvent être décrites. Si une adresse est placée dans une autre variable, on dit que cette autre variable est un pointeur (c'est-à-dire qu'il stocke l'adresse, ou « pointe vers » la « première » variable). Afin de profiter de la vérification des types et des opérations arithmétiques sur les pointeurs par le compilateur, les pointeurs sont déclarés avec le type d'élément qu'ils pointent, même si tous les pointeurs sont eux-mêmes de la même taille. Ainsi, un pointeur est correctement déclaré en tant qu'un pointeur vers un caractère (char) ou un pointeur vers un nombre à virgule flottante (float). L'opérateur * permet d'indiquer que la déclaration qui suit est un pointeur. Par exemple :
int
*
pc;
déclare un pointeur vers un type int.
int
c, *
pc;
c =
12
;
pc =
&
c;
Ce morceau de code déclare une variable de type int et un pointeur vers un type int, attribue 12 à la variable c, puis place l'adresse de c dans le pointeur pc. La valeur de l'élément pointé peut être récupérée via l'opérateur de déréférencement *. Considérons l'ajout suivant à ce qui précède :
printf
(
"
La valeur de c est %d, l'adresse de c est %d
\n
"
, c, &
c );
printf
(
"
La valeur de pc est %d, La valeur pointee par pc est %d
\n
"
, pc, *
pc );
Notez que c et *pc ont la même valeur (12) mais aussi, que si la valeur de c est modifiée, *pc reflète cette modification. Ainsi, le code supplémentaire suivant :
c =
36
;
printf
(
"
La nouvelle valeur de c est %d, l'adresse de c est %d
\n
"
, c, &
c );
printf
(
"
La valeur de pc est %d, La valeur pointee par pc est %d
\n
"
, pc, *
pc );
affichera 36 pour *pc ainsi que pour c, et les adresses ne seront pas modifiées.
Exercice 1 : créez un petit programme basé sur le code ci-dessus et exécutez-le. Comparez vos résultats avec ceux de vos collègues. Que remarquez-vous ?
Que se passe-t-il si l'adresse du pointeur est stockée dans un autre pointeur ? Que signifie l'adresse d'un pointeur stockée dans un autre pointeur ? C'est ce qu'on appelle un pointeur de pointeur. Testez le morceau de code suivant :
int
c, *
pc, **
ppc;
c =
12
;
pc =
&
c;
ppc =
&
pc;
printf
(
"
pc = %d, la valeur pointee par pc est %d
\n
"
, pc, *
pc );
printf
(
"
ppc = %d, la valeur pointee par ppc est %d
\n
"
, ppc, *
ppc );
Exercice 2 : modifiez votre programme pour implémenter ce qui précède. Exécutez-le et comparez vos résultats. Que pensez-vous de la valeur **ppc ?
Les adresses peuvent être envoyées en tant qu'arguments aux fonctions. C'est ainsi qu'une fonction peut renvoyer plus d'une valeur, c'est un concept important à retenir. Considérez ce qui suit :
int
main
(
void
)
{
int
a, b, c, *
pc;
pc =
&
c;
assign_it
(
&
a, &
b, pc );
printf
(
"
Les valeurs dans
\"
main
\"
sont : %d %d %d
\n
"
, a, b, c);
}
void
assign_it
(
int
*
x, int
*
y, int
*
z )
{
*
x =
1
;
*
y =
20
;
*
z =
300
;
}
Notez que assign_it() peut être appelée en utilisant soit l'adresse via l'opérateur (&) sur une variable existante, soit en passant un pointeur sur une variable (comme dans le cas de &c ou pc). En outre, la déclaration de assign_it() indique qu'elle accepte des pointeurs vers le type int, et non la valeur d'un int.
Exercice 3 : en utilisant ce qui précède comme exemple, créez un programme pour tester le retour de plusieurs valeurs à partir d'une fonction.
Exercice 4 : modifiez ce qui précède pour afficher les adresses des variables reçues par assign_it(), ainsi que &a, &b, et pc dans la fonction main(). Qu'est-ce que cela montre ?
Remarque : si vous n'êtes pas sûr de la taille des pointeurs dans un système d'exploitation donné, utilisez simplement la fonction sizeof(). Cela renverra la taille de l'élément en octets. Par exemple, le morceau de code,
int
x, *
pc;
x =
sizeof
(
pc );
printf
(
"
Les pointeurs font %d octets
\n
"
, x);
peut afficher 2 (Windows 3.1), 4 (Windows 95/XP, plusieurs systèmes UNIX) ou 8 (vrais systèmes d'exploitation 64 bits). Notez que pc peut être un pointeur vers un float, double, char ou n'importe quel autre type, il sera toujours de la même taille. C'est un point très important à comprendre. Essayez d'utiliser sizeof() pour vérifier cela.