les pointeurs en C
Un pointeur est une variable spéciale qui peut contenir l'adresse d'une autre variable. Chaque pointeur est limité à un type de données. Il peut contenir l'adresse d'une variable de ce type.
Pour utiliser des pointeurs en C, nous devons comprendre les deux opérateurs ci-dessous.
- Pour accéder à l'adresse d'une variable vers un pointeur, nous utilisons l'opérateur unaire & qui renvoie l'adresse de cette variable.
Par exemple, &x nous donne l'adresse de la variable x.Exemple 1 :
#include < stdio.h> int main(void){ int x; printf("adresse de x est : %p",&x); return 0; }
adresse de x est : 0x7ffee6ffea28 - Un autre opérateur est unary *, qui sert à deux choses :
Pour déclarer une variable de pointeur
Lorsqu'une variable de pointeur est déclarée en C / C ++, il doit précéder d'un *.
Exemple 2 :
#include < stdio.h> int main(void){ int x; // 1) Puisqu'il y a * dans la déclaration, // ptr devient un pointeur (une variable // qui stocke l'adresse d'une autre variable) // 2) Puisqu'il y a int avant *, ptr est // pointeur sur une variable de type entier int *ptr; // & opérateur avant que x est utilisé pour obtenir l'adresse de x // L'adresse de x est assignée à ptr. ptr = &x; return 0; }
Accéder à la valeur stockée dans l'adresse
Pour accéder à la valeur stockée dans l'adresse, nous utilisons l'opérateur unaire (*) qui renvoie la valeur de la variable située à l'adresse spécifiée par son opérande.
Exemple 3 :
#include < stdio.h> int main(void){ int x=2; // pointeur contenant l'adresse de x. int *ptr=&x; // La valeur à l'adresse est maintenant 5 *ptr = 5; printf(" *ptr = %d \n",*ptr); printf(" x = %d",x); return 0; }
*ptr = 5
x = 5
Déclaration d'un pointeur
- type: Type de données stocké dans l'adresse.
- nom_du_pointeur : nom du pointeur
Exemple 4 :
char *buffer; int *x; float *y;
Initialisation d'un pointeur
Il doit pointer vers une zone mémoire réservée par le compilateur comme étant une variable
Exemple 5 :
int f; int *pf,*pg; // création du lien entre le pointeur et la variable pf = &f; // allocation dynamique d'une variablede type int // création du lien entre pg et l'espace mémoire réservé pg =(int*)malloc(sizeof(int)); . . . free(pg); // libération de l'espace réservé
Utilisation des pointeurs
Les pointeurs ont un grand nombre d'intérêts :
- Ils permettent de manipuler de façon simple des données pouvant être importantes (au lieu de passer à une fonction un élément très grand (en taille) on pourra par exemple lui fournir un pointeur vers cet élément...)
- Les tableaux ne permettent de stocker qu'un nombre fixé d'éléments de même type. En stockant des pointeurs dans les cases d'un tableau, il sera possible de stocker des éléments de taille diverse, et même de rajouter des éléments au tableau en cours d'utilisation (la notion de tableau dynamique) ce qui n’est pas possible pour les tableaux statiques.
- Il est possible de créer des structures chaînées.
Après (et seulement après) avoirdéclaré et initialisé un pointeur, il est possible d'accéder aucontenu de l'adresse mémoire pointée par le pointeur grâce à l'opérateur '*'
Si P un pointeur, on doit distinguer *P et &p : &P contient l’adresse de la variable dont le pointeur P pointe, et *P permet d’accéder à la valeur de la variable sur laquelle pointe P.
Exemple 6 :
int*P ; int A=10 ; // le pointeur P pointe sur A P=&A ; // équivalent à A=A+1 *P=*P+1 ; // équivalent à A=5 *P=5 ;
Les opérations élémentaires sur les pointeurs
Priorité de * et &
Les opérateurs * et & ont la même priorité que les autres opérateurs unaires (la négation !, l'incrémentation ++, la décrémentation --). Dans une même expression, les opérateurs unaires *, &, !, ++, -- sont évalués de droite à gauche.
Arithmétique de pointeur
Un ensemble limité d'opérations arithmétiques peut être effectué sur des pointeurs.
Un pointeur peut être:
- incrémenté (++)
- décrémenté (--)
- un entier peut être ajouté à un pointeur (+ ou + =)
- un entier peut être soustrait d'un pointeur (- ou - =)
L'arithmétique de pointeur n'a pas de sens si elle n'est pas effectuée sur un tableau.
Exemple 7 :
int x,y; int *P; P=&x; // y = x + 1 y = *P+1; // X = X + 10 *P = *P+10; // X += 2 *P += 2; // X++ (*P)++;
Exemple 8 :
int *P; P = 0;
Exemple 9 :
Soit p1 et p2 deux pointeurs sur int.
#include < stdio.h> int main(void){ int x=5; int *p1, *p2; p2=&x; // Copie le contenu de p2 vers p1 // p1 pointe alors sur le même objet que p2. p1=p2; printf("*p1 = %d \n", *p1); printf("*p2 = %d \n", *p2); return 0; }
*p2 = 5
Pointeurs et les tableaux
Lien entre le nom d'un tableau à 1 dimension et les pointeurs
Les pointeurs et les tableaux sontconceptuellement très similaires en C
Nom du tableau = adresse du premier élément du tableau.
En simplifiant, nous pouvons retenir que le nom d’un tableau est un pointeur constant sur le premier élément du tableau.
Exemple 10 :
En déclarant un tableau A de type int et un pointeur P sur int,
#include < stdio.h> int main(void){ int A[10]; int *P; // equivalente à P =&A[0]; P = A; return 0; }
Si P pointe sur une composante quelconque d'un tableau, alors P+1 pointe sur la composante suivante.
Généralement P+i pointe sur la i-ième composant devant P.
Exemple 11 :
int main(void){ int A[10]; int *P; int x, i=6; // Le pointeur P pointe sur A[0] (P =&A[0]) P = A; // x = A[1] x = *(P+1); // x = A[2] x = *(P+2); // x = A[i] x = *(P+i); return 0; }
Puisque le nom tableau est un pointeur constant sur le premier élément on peut écrire :
Exemple 12 :
int main(void){ int A[10]; int x, i=6; // x = A[0] x = A; // x = A[1] x = *(A+1); // x = A[2] x = *(A+2); // x = A[i] x = *(A+i); return 0; }
- Un pointeur est une variable, donc des opérations comme P = A ou P++ sont permises.
- Le nom d'un tableau est une constante, donc des opérations comme A = P ou A++ sont impossibles.
Exemple 12 :
Initialiser les éléments du tableau Tab à 1 (version sans pointeur)
int main(void){ int Tab[10]; int i; for(i=0 ; i < 10 ; i++){ Tab[i]=1; } return 0; }
Exemple 14 :
Initialiser les éléments du tableau Tab à 1 avec les pointeurs (V1)
int main(void){ int Tab[10], i; int *P; P=Tab; for(i=0 ; i < 10 ; i++){ *(P+i) = 1; } return 0; }
Exemple 15 :
Initialiser les éléments du tableau Tab à 1 avec les pointeurs (V2)
int main(void){ int Tab[10]; int *P; P=Tab; for(P=Tab ; P < (Tab+10) ; P++){ *P = 1; } return 0; }
Lien entre le nom d'un tableau à 2 dimension et les pointeurs
un tableau 2d est un tableau de tableau de dimension 1
- On peut donc dire que le nom d'un tableau 2d est l'adresse d'un tableau d'adresse de tableaux de dimension 1
- On dit aussi qu'il s'agit d'un pointeur de pointeurs
Exemple 16 :
int TAB[6][7];
On suppose Tab a deux dimensions défini comme suit:
int Tab[3][4] = {{ 0, 1, 2, 3}, {10,11,12,13}, {20,21,22,23}};
- Le nom du tableau Tab représente l'adresse du premier élément du tableau et pointe sur le tableau Tab[0] qui a les valeurs: {0,1,2,3}
- L'expression (Tab+1) est l'adresse du deuxième élément dutableau et pointe sur Tab[1] qui a les valeurs: {10,11,12,13}
En général, Tab[i][j] est équivalent à *(*(Tab + i) + j)
La mémoire d’un ordinateur étant organisée de manière linéaire, il n’est pas possible de stocker le tableau à deux dimensions en lignes et en colonnes. Le concept de lignes et de colonnes n’est que théorique; en fait, un tableau à deux dimensions est stocké dans un ordre de rang majeur, c’est-à-dire que les rangées sont placées les unes à côté des autres. La figure suivante montre comment le tableau 2D ci-dessus sera stocké en mémoire.
Exemple 17 :
Initialiser les éléments d'un tableau Tab à deux dimensions à 1 (version sans pointeur)
int main(void){ int Tab[5][4]; int i, j; for(i=0 ; i < 5 ; i++){ for(j=0 ; j < 4 ; j++){ Tab[i][j]=1; } } return 0; }
Exemple 18 :
Initialiser les éléments du tableau Tab à 1 avec les pointeurs
int main(void){ int Tab[5][4]; // créer un pointeur de 5 éléments (nombre de ligne de Tab) int *P[5]; int i, j; // pointer chaque élément (i) de P vers le premier élément // de chaque ligne i de Tab for (i = 0; i < 5; i++) { P[i] = Tab[i]; } // remplir le tableau par des 1 for (i = 0; i < 5; i++) { for (j = 0; j < 4; j++) { *(*(P + i) + j) = 1; } } return 0; }
Pour cet exemple nous avons utilisé un tableau des pointeurs *P[5] chaque pointeur P[i] pointe sur une ligne Tab[i] .
Exemple 19 :
Dans la mémoire les éléments d’un tableau à deux dimensions sont adjacents, on peut utiliser un pointeur qui pointe sur le premier élément du tableau et ensuite déplacer ce pointeur sur les autres éléments du tableau.
int main(void){ int Tab[5][4]; int *P; int i, j; P=Tab[0]; // remplir le tableau par des 1 for (i = 0; i < (5*4); i++) { *(P+i)=1; } return 0; }