print · rss · source

< Chaînes de caractères | TutoCFrench | Passer des arguments à un programme >


Les pointeurs

Notions :

  • Adresse mémoire
  • Opérateurs * et &
  • Passage d'arguments par adresse
  • Pointeurs de pointeurs de ...
  • Type void* (pointeurs generiques)
  • const int *p et int * const p

La notion de pointeur a la réputation d'être difficile à comprendre et encore plus à maîtriser. Il s'agit en fait d'une notion extrèmement simple : un pointeur est une variable qui stocke pour valeur une adresse mémoire :

Déclaration

On déclare un pointeur d'un type donné en ajoutant le signe * avant le nom du pointeur. Dans l'exemple ci-dessous, ptr stocke l'adresse d'une donnée de type char et ptr2 stocke l'adresse d'une donnée de type int.

char *ptr;
int* ptr2; /* possible aussi ! */

L'opérateur &

L'opérateur & sert à récupérer l'adresse mémoire d'une variable.
Dans l'exemple ci-dessous, on affiche l'adresse mémoire d'une variable :

int var = 4;
printf("L'adresse de 'var' est %p\n", &var);

Dans l'exemple ci-dessous, on récupère l'adresse mémoire d'une variable pour l'affecter à une variable de type pointeur :

int var = 4;
int *ptr;   /* declaration de ptr en tant que pointeur sur 'int' */
ptr = &var; /* 'ptr' pointe sur la variable 'var' */
printf("L'adresse de 'var' est %p\n", ptr);

Il est aussi possible de pointer sur l'élément d'un tableau :

int tab[5];
int *p;
p = &tab[4]; /* ptr pointe sur le dernier element du tableau */

L'opérateur *

Un pointeur contient une adresse en mémoire, et grâce à l'opérateur *, on peut accèder à la valeur contenue à cette adresse (soit pour la modifier, soit pour l'examiner) :

int var = 5;
int *p;
p = &var;
printf("La valeur de 'var' est %d\n", *p);

L'exemple ci-dessous illustre comment on peut modifier le contenu d'une variable indirectement, par son pointeur :

int var = 5;
int *p;
p = &var;
*p = 4;
printf("La valeur de 'var' est %d\n", var);

Manipulation de pointeurs

Les pointeurs peuvent être incrémentés, décrémentés, additionnés ou soustraits. Dans ce cas, leur nouvelle valeur dépend de leur type. Incrémenter un pointeur de char ajoute 1 à sa valeur. Incrémenter un pointeur de int ajoute 2 ou 4 (cela dépend de l'architecture).
Cette arithmétique appliquée aux pointeurs est utile quand on manipule des tableaux. En incrémentant un pointeur sur un élément d'un tableau, on pointe sur l'élément suivant :

int tab[] = {18, 21, 33};
int *p;

p = &tab[0]; 
printf("%d\n", *p); /* Affiche '18' */

p++;
printf("%d\n", *p); /* Affiche '21' */

Allez plus loin avec l'opérateur *

Pour reprendre l'exemple ci-dessus, on peut accèder aux éléments d'un tableau sans altèrer la valeur de p, grâce à une autre écriture :

p = &tab[0];
printf("%d\n", *p); /* Affiche '18' */
printf("%d\n", *(p+1)); /* Affiche '21' */
printf("%d\n", *(p+2)); /* Affiche '33' */

L'opérateur []

L'opérateur crochet s'applique aussi aux pointeurs. Il permet de faire comme si un pointeur était un tableau. Ceci permet de beaucoup simplifier les écritures :

p = &tab[0];
printf("%d\n", p[0]); /* Affiche '18' */
printf("%d\n", p[1]); /* Affiche '21' */
printf("%d\n", p[2]); /* Affiche '33' */

Pointeurs, tableaux et chaînes littérales

Nous avons vu qu'il est possible de récupèrer l'adresse mémoire d'un élément d'un tableau. Mais nous pouvons aller encore plus loin ! En effet, un nom de tableau utilisé dans une expression est converti en une adresse sur le début de ce tableau :

int tab[] = {18, 21, 33};
int *p;
p = tab; /* equivalent a : "p = &tab[0]" */

Quand une chaîne littérale est utilisée dans une expression, elle est convertie en une adresse sur le début de la chaîne :

int *ptr = "abcd";
printf("%s\n", ptr);

Pointeurs génériques

Les pointeurs de type void * sont utilisés pour pointer sur quelque chose dont on ne connais pas le type. Les seuls opérateurs autorisés avec les pointeurs génériques sont :

  • l'affectation (opérateur =)
  • la conversion de type

Les autres opérateurs sont interdits. Parmi eux :

  • l'indirection : *p
  • l'addition : p+i
  • la soustraction : p-i

Pointeurs et fonctions : le passage par adresse

Nous avons déjà vu la notion de passage par valeur qui ne permet pas à une fonction de modifier la valeur d'une variable passée en argument.
Grâce aux pointeurs, nous pouvons créer des fonctions qui modifient directement une variable passée en argument ! On appelle cette notion le passage par adresse.

Par exemple, la fonction ci-dessous incrémente la variable dont l'adresse est passée en argument :

void incr(int *n)
{
    *n = *n + 1;
}

int main()
{
    int a = 3;
    incr(&a); /* 'a' est incrementee */
    printf("%d\n", a);
    return 0;
}

L'appel à la fonction incr() fonctionne en plusieurs étapes :

  1. L'appel à la fonction incr() prend en argument l'adresse de la variable a (l'opérateur & retourne son adresse)
  2. L'adresse de a est stockée dans la variable n de la fonction incr()
  3. L'opérateur * permet d'accèder au contenu de la case mémoire pointée par n. En l'occurence, ici, on incrémente cette valeur
  4. La fonction retourne à la fonction principale main() qui affiche la nouvelle valeur de a

Remarque : un tableau est toujours passé par adresse dans une fonction. Quand une fonction prend en argument un tableau, elle travaille toujours sur l'original !

Utilisation avancée

int **p; /* pointeur sur un pointeur d'entier */
int *t[10]; /* tableau de 10 pointeurs sur des entiers */
int (*pt) [5]; /* pointeur sur un tableau de 5 entiers */
int *fonc(); /* fonction renvoyant un pointeur sur un entier */
int (*pfonc) (); /* pointeur sur une fonction retournant un entier */

Exercices

  1. "echange.c" : fonction qui échange les valeurs de deux entiers passés en argument

Solutions

echange.c

#include <stdio.h>

void echange(int *a, int *b)
{
   int tmp;
   tmp = *a;
   *a = *b;
   *b = tmp;
}

int main()
{
    int a, b;

    printf("a> ");
    scanf("%d", &a);

    printf("b> ");
    scanf("%d", &b);

    printf("after...\n");
    printf("a = %d\nb = %d\n", a, b);

    return 0;
}

< Chaînes de caractères | TutoCFrench | Passer des arguments à un programme >

print · rss · source
Page last modified on February 11, 2008, at 03:43 PM