Next: 8 Entrées-sorties
Up: Le langage C
Previous: 6 Objets structurés
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
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.
p = &c;
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.
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).
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
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;
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
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
pint = &tab[0]; ou pint = tab;
é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; }
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.
La notation p+5 doit être comprise comme p+5*sizeof(*p) en octets.
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 */
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:
|
|
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 */
est vraie si p pointe sur un élément du tableau qui précède celui sur lequel pointe q.
p < q
Tout autre opération que ceux énoncés plus haut est interdite.
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); }
Touraivane