• Aucun résultat trouvé

Définition et affectation d’un pointeur

13.1.1 Définition d'un pointeur

Considérons la définition : short int n = 3 ;

n représente une variable du type entier, c'est-à-dire une adresse en mémoire dont le contenu est un entier

de valeur 3. Cette définition a pour but de demander au compilateur/linker de réserver un emplacement mémoire, à une certaine adresse, pour la variable n.

Le contenu d'une variable peut être un entier, un réel, un caractère. Mais, c’est nouveau, ce contenu peut aussi être une adresse. Considérons les définitions suivantes :

short int* AdrEntier ;

double* AdrReel ; ou double *AdrReel ;

Cette écriture short int* signifie que AdrEntier est une variable dont le contenu est l'adresse d'une

variable entière, tandis que AdrReel contient l'adresse d'une variable réelle.

On dit que AdrEntier est un pointeur sur un entier et AdrReel un pointeur sur un réel.

Ces définitions réservent la place mémoire nécessaire pour écrire deux adresses (une dans AdrEntier, l'autre dans AdrReel), mais elles ne réservent pas de place en mémoire pour les variables pointées (qui doivent être allouées séparément). De plus, AdrEntier et AdrReel, comme toute variable, ne sont pas initialisées a priori.

13.1.2 Affectation d'un pointeur

Soit un pointeur ptr et une variable var définis par :

short int var ; short int* ptr ;

L'étoile * peut être collée à un des deux mots qui l'entourent ou laissée isolée. Personnellement, je trouve double* ptr plus clair que double * ptr, car le type de ptr est bien double *.

Pour que ptr pointe sur var, il faut que le contenu de ptr soit l'adresse de var. Il suffit donc d'utiliser l'opérateur d'adresse & pour réaliser l'affectation :

ptr = &var ; /* à présent, ptr pointe sur var */

On peut ensuite manipuler la valeur de var à l'aide du pointeur sur var, par adressage indirect. L'instruction :

*ptr = 13 ;

signifie : mettre la valeur 13 dans la « variable pointée par ptr » (c’est-à-dire var).

*ptr désigne le contenu de l'adresse ptr. * est ici l’opérateur d’indirection.

Figure 13 --4 : Pointeur et variable pointée

En pratique, on dispose à l’intérieur d’une fonction, soit de la variable toto (adressage direct), soit de l’accès par pointeur *ptr_toto (adressage indirect). Mais jamais des deux à la fois ! Il ne s’agit donc pas de créer des pointeurs juste pour le plaisir de compliquer les choses… Le pointeur est un moyen de manipuler une donnée qui n’est pas accessible autrement : parce qu’elle est définie dans une autre fonction ou parce qu’elle est créée par allocation dynamique.

Le programmeur débutant doit utiliser pour un pointeur un nom qui commence par p_ ou ptr_, afin de ne pas oublier la nature particulière de cette variable et la nécessité d'utiliser l'étoile * pour accéder à la variable pointée.

Seule exception à cette règle de prudence : quand le pointeur est utilisé pour pointer sur un tableau. Nous verrons en effet que les crochets contiennent déjà « l'indirection », et que l'étoile * ne sera pas utilisée pour accéder à la variable pointée quand il s'agit d'un élement de tableau. L'écriture tab[i] sera alors plus facile à utiliser que ptr_tab[i].

L'affectation ptr=&var est une première façon (rarement utilisée sous cette forme) de donner une valeur à un pointeur. Il en existe d'autres, en particulier l'allocation dynamique : celle-ci consiste à réserver de la place en mémoire au cours de l'exécution et à placer l'adresse de cette zone mémoire dans un pointeur, dont la valeur n'est donc connue qu'à l'exécution.

L'allocation dynamique sera étudiée en détail au paragraphe 13.4, mais nous résumons ici son principe. Elle s'effectue à l'aide de l'instruction malloc dont la syntaxe est :

toto = 10 ; adressage direct est équivalent à

*ptr_toto = 10 ; adressage indirect

ptr

F3C5

F3C5

13

var

pointeur

*ptr

#include <stdlib.h> /*contient le protype de malloc */ type_var* ptr ;

ptr = (type_var*) malloc( nb_octets ) ;

L'instruction ptr=( ...)malloc(nb_octets) réserve de la place en mémoire pour nb_octets octets et

place l'adresse correspondante dans le pointeur ptr. Cette allocation mémoire a lieu au cours de l'exécution, c'est pourquoi on dit qu'elle est dynamique.

Contrairement à ptr=&a, qui place dans le pointeur l'adresse d'une variable connue du compilateur,

l'affectation du pointeur par malloc utilise l'adresse d'un « objet » qui n'existe pas encore à la compilation, et qui sera créé à l’exécution.

L'allocation mémoire sera étudiée plus en détail au paragraphe 13.4.

Exemple 78. Adresse et contenu de variables (intérêt pédagogigue seulement)

A la fin de ce programme, voici le contenu de la mémoire :

20

a

10

b

ptr

0xFFCC 0xFFCE 0xFFD0 0xFFCC

Ce programme affiche par exemple (avec l'allocation mémoire du schéma ci-dessus) :

adresse de a : FFCC (65484 en décimal)

adresse de b : FFCE (65486 en décimal) --> un entier court occupe 2 octets adresse de ptr : FFD0 (65488 en décimal)

contenu de ptr : FFCC --> on voit bien que ptr pointe sur a

contenu de a : 20 contenu de b : 10

contenu de la variable pointée par ptr : 20 void main(void)

{

short int a,b ; short int* ptr ;

ptr = &a ; /* ptr pointe sur a */ a = 10 ;

b = *ptr ; /* met dans b le contenu de l'adresse ptr (10) */

*ptr= b*2 ; /* met la valeur b*2 à l'adresse pointée par ptr (attention aux 2 sens différents de *) */

printf(" adresse de a : %p (%u en décimal)", &a, &a) ; printf("\n adresse de b : %p (%u en décimal)", &b, &b) ;

printf("\n adresse de ptr : %p (%u en décimal)", &ptr, &ptr) ; printf("\n\n contenu de ptr : %p", ptr) ;

printf("\n\n contenu de a : %hd \n contenu de b : %hd", a, b) ; printf("\n contenu de la variable pointée par ptr : %hd", *ptr) ; getch() ; /* attend la frappe d'une touche */

13.1.3 Quelques pièges

On prendra garde à ne pas confondre un pointeur (ptr) avec l'objet qu'il pointe (*ptr). L'exemple suivant, qui n’a qu’un intérêt pédagogique, illustre quelques manipulations courantes de pointeurs.

Exemple 79. Jeu avec des pointeurs (cet exemple ne sert qu’à illustrer l’utilisation de *)

Il ne faut pas confondre les écritures suivantes :

• ptr1 = ptr2 est une affectation entre pointeurs : ptr1 et ptr2 contiennent désormais la même adresse (ils pointent sur la même variable) ;

• *ptr1 = *ptr2 copie le contenu de l'emplacement mémoire pointé par ptr2 dans celui pointé par ptr1. Les deux pointeurs ne contiennent pas les mêmes adresses ;

• *ptr1=0 met à 0 le contenu de l'adresse pointée par ptr1 ;

• ptr1=0 ou mieux ptr1=NULL signifie par convention que ptr1 ne pointe sur rien. Il est donc interdit de l’utiliser tant qu’il n’a pas reçu de valeur.