Exood4 Studio - Video Game Development, Toulouse (France)
Exood4 Studios Exood4 Tutorials

   
L a n g a g e   C

L e s   p o i n t e u r s






L'opérateur de référence


 L'opérateur de référence & retourne l'adresse de l'opérande qui doit être une lvalue.

 Si a est une expression de type T, &a est de type pointeur de T, le résultat de l'opérateur & n'est jamais une lvalue. Par exemple si i est de type int, &i est l'adresse de i et de type pointeur d'entier. Par contre, &(i+1) est incorrect puisque i+1 n'est pas une lvalue.


L'opérateur d'indirection


 L'opérateur d'indirection * retourne l'objet pointé par l'opérande qui doit être une adresse.

 Si a est de type pointeur de T, *a est de type T. Le résultat de l'opérateur * est toujours une lvalue. Par exemple, si ptr est de type pointeur d'entier, *ptr est de type entier et sa valeur est l'entier situé à l'adresse ptr.

 L'opérateur * appliqué à un pointeur nul donne un résultat imprévisible (provoque le plus souvent une erreur d'exécution).


Le type pointeur


 Pour tout type T, on peut créer un type pointeur de T. Une valeur de ce type correspond à une adresse d'un objet de type T.

 T* identificateur;

 De façon générale si T est un type, T* est le type pointeur de T. La taille de la zone référencée dépend bien sûr de T. Par contre la taille d'une variable de type T* ne dépend pas de T, mais dépend de l'architecture de la machine.


Exemple :

int* ptr;  /* variable pointeur d'entier */
int *ptr;  /* cette notation est préférable, en effet la première peut prêter à confusion */
int* a,b; /* ne définit pas 2 pointeurs, mais un pointeur d"entier (a) et un entier (b) */
int *f();  /* fonction retournant un pointeur d'entier */
int (*f) (); /* pointeur d'une fonction retournant un entier */
void *ptr;  /* pointeur sans type */

 Les opérateurs & et * peuvent alors s'utiliser de la façon suivante :

int i, *ptr;

ptr = &i;
*ptr = 11;

 Cependant il n'est pas possible d'effectuer une indirection sur un pointeur de type void :

void *ptr;
ptr = 1; /* incorrect */

 Il existe une constante pointeur NULL, qui ne pointe sur rien. La constante NULL n'est autre que l'entier 0.

if (ptr) i = *ptr;

est équivalent à :

if (ptr != NULL) i = *ptr;


 Le nom d'une fonction est converti en un pointeur sur cette fonction. De même, le nom d'un tableau est converti en un pointeur sur son premier élément.

 En C, "pointeur sur" est synonyme de "adresse de". Il est néanmoins conseillé de différencier une variable qui contient une adresse fixe (par exemple l'adresse de début d'un tableau) d'une autre contenant une adresse variable (adresse de l'élément courant d'un tableau). Cette différence peut être mise en évidence par leur nom, par exemple en commençant la première par adr (adresse fixe) et la seconde par ptr (adresse variable).

char *adrTab = tab;
char *ptrTab = &tab[3];

 Une autre solution consiste à utiliser le spécificateur const, on peut alors être sûr que toute variable adresse restera constante (ne sera pas modifiée) :

char * const adrTab = tab;

adrTab = &tab[3]; /* instruction interdite */
adrTab[2] = 'a';    /* instruction correcte */

const char *adrTab = tab; /* pointeur sur une constante */
adrTab[2] = 'a';    /* instruction interdite */
adrTab = &tab[3]; /* instruction correcte */

const char * const adrTab = tab; /* constante pointeur pointant sur une zone constante */



Les opérations sur les pointeurs


Addition et soustraction avec un entier

 Si p est de type pointeur de T, et i un entier, p+i est l'adresse du ième élément de type T situé après l'adresse p. Autrement dit, c'est la valeur de p à laquelle est ajoutée i * sizeof(T). Le résultat est correct et portable si l'objet pointé par p est situé dans un tableau et que p+i reste situé dans les limites du tableau. La soustraction avec un entier suit le même principe.

 Ces opérations sont impossibles avec les pointeurs de type void *.


Soustraction de 2 pointeurs

 Si p et q sont deux pointeurs de même type, p-q est l'entier k tel que p+k donne q. Le résultat est correct et portable si p et q sont situés dans le même tableau : k est alors le nombre d'éléments qui les séparent.

 Ces opérations sont impossibles avec les pointeurs de type void *.


Initialisation

 Un pointeur, comme toute variable, doit être initialisé avant d'être utilisé.

int *ptr;
*ptr = 1; /* affecte la valeur 1 à une adresse indéfinie */

 Il est possible d'initialiser directement un pointeur à une adresse, ce qui permet d'accéder à des zones bien précises du système. Mais on rencontre le plus souvent l'initialisation d'un pointeur à une adresse calculée à partir de celle d'un autre objet :

int i, *adr = &i, *ptr;
ptr = &i - 1;

ou à partir d'un autre pointeur :

int *ptr = adr;


 Certaines fonctions permettent d'initialiser un pointeur. C'est le cas des fonctions d'allocation de mémoire.


La gestion dynamique de la mémoire

 Un programme peut gérer en cours d'exécution ses besoins en mémoire : une demande de réservation est envoyée au système qui, dans la mesure du possible, alloue une zone et renvoie son adresse. Par ce mécanisme, un programme a la possibilité de demander, plusieurs fois lors de son exécution, de la mémoire neuve et peut également la libérer lorsqu'elle devient inutile.

 C'est le rôle de la fonction malloc, qui admet comme paramètre un entier correspondant à la taille de la zone souhaitée. Si l'allocation n'est pas possible, la fonction malloc retourne NULL, sinon elle renvoie l'adresse du début de la zone allouée :

int *adr_zone;

adr_zone = malloc (100 * sizeof(int));
if ( adr_zone == NULL )
{
 puts ("mémoire insuffisante");
 exit (1);
}

 Toute zone allouée dynamiquement peut être libérée, à l'aide de la fonction free :

free (adr_zone);

Le pointeur n'étant pas transmis par adresse, free ne peut le modifier (pour le mettre à NULL par exemple) et sa valeur est par conséquent conservée. Attention à ne pas réutiliser la valeur d'un pointeur après libération de la mémoire !


  Retour en haut de page Page suivante