next up previous contents index
Next: 8 Entrées-sorties Up: Le langage C Previous: 6 Objets structurés

Subsections


7 Pointeurs

  

7.1 Introduction

En simplifiant beaucoup, on peut dire qu'un programme possède un tableau de cases mémoires (octets) consécutives que l'on manipule individuellement ou par groupe. C'est ce tableau qui constitue la mémoire disponible pour un programme. Un pointeur n'est rien d'autre qu'une suite de cases (4 octets en général). Ainsi, si c désigne une variable de type caractère et pc un pointeur vers c, on peut représenter la mémoire de la manière suivante:

Un pointeur est une variable qui contient l'adresse d'une autre variable.

        Un pointeur est une variable qui contient l'adresse d'une autre variable. L'opérateur unaire & donne l'adresse mémoire d'un objet; l'instruction

 
p = &c;
affecte l'adresse de c à la variable p. On dit que p pointe sur c. L'opérateur unaire & s'applique uniquement sur des objets en mémoire. En particulier, il ne peut s'appliquer sur des constantes ou même à des variables déclarées comme register 5.1.2.

L'opérateur unaire * représente l'opérateur d'indirection ou de déréférence. Appliqué à un pointeur, il donne accès à l'objet pointé par ce pointeur.

 
int x = 1, y = 2, z[10];
int *pi;       pi pointeur sur un int
pi = &x;       pi pointe sur x
y = *pi;       y est affecté à la valeur de l'objet pointé par pi i.e. x
*pi = 0;       x vaut 0 
pi = &z[0];    pi pointe sur le premier élément du tableau z
La définition du pointeur int *pi spécifie que *pi est un int. Un pointeur adresse forcément un objet d'un type bien particulier. Il y a, cependant, une exception avec les pointeurs void * que nous verrons plus loin (section 7.5).

Si pi pointe sur une variable x, alors *pi et x désigne la même chose et on pourra utiliser indifférement l'une ou l'autre des notations.

 
*pi = *pi + 3;      équivalent à        x = x +3; 

7.2 Pointeurs et passage par adresse

      Comme nous l'avons déjà dit dans la section 4.2.2, pour conserver sur les arguments effectifs, les modifications effectuées par une fonction sur les arguments formels, il faut que les arguments soient passés par adresse.

Pour échanger les valeurs de deux variables à l'aide d'une fonction, on écrira:

 
void echanger(int *px, int *py) {
    int tmp;
    temp = *px; *px = *py; *py = tmp;
}

main() { int x = 10, y=20; printf("%d %d +n", x, y); x=10, y=20 echanger(&x, &y); printf("%d %d +n", x, y); x=20, y=10 }

Le passage par adresse d'un argument n'est rien d'autre qu'un passage par valeur de son adresse

7.3 Pointeurs et tableaux

  Les pointeurs et les tableaux sont conceptuellement très similaires en C. A l'exception des tableaux multidimensionnels, toute opération que l'on effectue par indexation dans un tableau peut être réalisée avec des pointeurs. Soient tab un tableau de int de 10 éléments et pint un pointeur vers un int. L'affectation
 
pint = &tab[0]; ou pint = tab;
fait en sorte que tab et pint pointe tous deux sur le même élément (le premier élément du tableau). Si pint pointe sur le ième élément du tableau, alors *(pint + 1) et *(pint - 1) désignent respectivement sur le i+1ème et ième

élément du tableau (voir figure 7.2).

La correspondance entre pint et tab est si étroite que l'on pourra écrire tab[i] ou *(pint +i)[*]. De même, &tab[i] ou pint +i sont équivalents.

Malgré les simulitudes, un pointeur et un tableau ne sont pas équivalents. En effet,
  • Un tableau alloue de la place en mémoire
  • Un tableau n'est pas une lvalue et ne peut donc pas figurer en membre gauche d'une affectation.

  Voici à présent la fonction strlen qui utilise les pointeurs plutôt que des tableaux:

 
int strlen(char *chaine) {
    register int i=0;
    while (*(chaine+i) != 0) 
        i++;
    return i;
}
 
int strlen(char *chaine) {
    char * s=chaine;
    while (*chaine++ != 0) ;
    return chaine - s - 1;
}

7.4 Arithmétique des pointeurs

 

Ajouter ou soustraire un entier à un pointeur.

Nous avons vu que, si p est un pointeur vers un tableau de caractères alors p+1 pointe sur le deuxième élément du tableau i.e l'octet suivant le premier élément. Qu'en est-il si, au lieu pointer sur un tableau de carcatères, p est un pointeur vers un tableau d'entiers longs (long) ou même un tableau dont les éléments sont des struct. Le langage C est cohérent en ce sens qu'une fois donné la structure de donnée pointée par p, p+1 désigne toujours l'élément suivant quelque soit sa taille.

 
char tabc[2], *pc=tabc;
long tabi[2], *pi=tabi;
double tabi[2], *pi=tabi;
printf("Adresse du deuxieme element de tabc est %d", pc+1);  /* pc + 1 octet  */
printf("Adresse du deuxieme element de tabi est %d", pi+1);  /* pi + 4 octets */
printf("Adresse du deuxieme element de tabd est %d", pd+1);  /* pd + 8 octets */
La notation p+5 doit être comprise comme p+5*sizeof(*p) en octets.

Soustraire deux pointeurs de même type

 

Outre l'addition d'un pointeur par une constante, il est également permis de soustraire deux pointeurs de même type comme dans l'exemple suivant:



 
int strlen(char *chaine) {
register char *c = chaine;
while (*c != 0) 
c++;
return c - chaine;
}





 
int strlen(char *chaine) {
register char *c = chaine;
while (*c++ != 0) ;
return c - chaine - 1;
}



Ici également, cette différence de pointeurs est fonction du type de l'objet pointé par ces pointeurs.

 
char tabc[2], *pc=tabc;
long tabi[2], *pi=tabi;
double tabi[2], *pd=tabd;
printf("la taille de l'objet pointe par pc est de %d octets", (pc+1)-pc);  /* 1 */
printf("la taille de l'objet pointe par pi est de %d octets", (pi+1)-pi);  /* 1 */
printf("la taille de l'objet pointe par pd est de %d octets", (pd+1)-pd);  /* 1 */

Comparaison de pointeurs de même type

  Il est egalement permis de comparer deux pointeurs de même type. Par exemple, la condition
 
p < q
est vraie si p pointe sur un élément du tableau qui précède celui sur lequel pointe q.

Tout autre opération que ceux énoncés plus haut est interdite.

7.5 Le type void

   

 Le type void se comporte (syntaxiquement) comme une type fondamental. Mais il n'existe aucun objet de type void. Il sert à préciser qu'une fonction ne renvoie aucune valeur (procédure), ou bien il peut être utilisé comme type de base pour des pointeurs sur des objets de type inconnu.

 
void f();
void * pv;

Un pointeur de n'importe quel type peut être affecté à un pointeur void (void *). Cette facilité doit être utilisée pour se servir des fonctions génériques qui rendent des pointeurs vers n'importe quel type d'objets, comme c'est le cas de la fonction malloc.  

 
void * malloc(unsigned taille);
void free(void *);

void une_fonction() { int *pi; char *pc; pi = (int *) malloc(10*sizeof(int)); Noter la conversion explicite pc = (char *) malloc(10*sizeof(char)); ... free(pi); free(pc); }

7.6 Allocation dynamiques et tableaux multi dimensionnels

7.7 Pointeurs de fonctions


next up previous contents index
Next: 8 Entrées-sorties Up: Le langage C Previous: 6 Objets structurés

Touraivane
9/21/1998