Chapitre III

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 33

III - Les pointeurs

94
1 Introduction
— Les variables utilisées dans un programme sont stockée
quelque part en mémoire centrale constituée d'octets
adjacents identifiés par un numéro unique : adresse.

— Retrouver variable = connaître l'adresse du 1er octet.


— Rappel :
— & appliqué à une variable retourne l'adresse-mémoire de cette
variable : &variable.

95
1 Introduction
— Quand on écrit :
int x  =  3;
printf("x  =  %d  et  se  trouve  dans  l’adresse  :  %d\n",  x,  &x);

x = 3 et se trouve dans l’adresse : 2293572

— Ce code réserve 4 octets pour la variable x dans la mémoire


(int sont codés sur 4 octets) à partir de 2293572 et
l’initialise avec la valeur 3

Rappel : En C, l’opérateur fonctionnel sizeof(Type) retourne


le nombre d’octets occupé par le type Type.
96
2 Adresse et valeur d’un objet
— On appelle Lvalue (left value) tout objet placé à gauche de
l’opérateur d'affectation (=).
— Lvalue est caractérisée par :
— son adresse : à partir de laquelle l'objet est stocké ;
— sa valeur : donnée stocké à cette adresse.

97
2 Adresse et valeur d’un objet
— Exemple :
int i,  j;
i  =  3;
j  =  i;

— Si le compilateur a placé la variable i à l'adresse 2293572 en


mémoire, et la variable j à l'adresse 2293568, on a :
objet adresse valeur
i 2293572 3
j 2293568 3

98
3 Notion de pointeur
— Pointeur : objet (Lvalue) dont la valeur = une adresse.
— Déclaration:
Type *  nomPointeur;

— Type est le type de l'objet pointé.


— Cette déclaration d’un identificateur, nomPointeur, associé à
un objet dont la valeur est l'adresse d'un autre objet de type
Type.
— nomPointeur = un identificateur d'adresse.

Comme pour n'importe quelle Lvalue, la valeur d’un


pointeur est modifiable.

99
3 Notion de pointeur
— Exemples :
int *p1;              /*  pointeur  sur  une  variable  de  type  entier  */
float *p2;          /*  pointeur  sur  une  variable  de  type  float */
Eleve *p3;          /*  pointeur  sur  une  variable  de  type  Eleve */
double **p5;      /*  pointeur  sur  un  pointeur  sur  un  double!    */

100
3 Notion de pointeur
— L'exemple suivant, définit un pointeur p sur un entier i :

int i =  3;
int *p;
p  =  &i;

— On se trouve dans la configuration :


objet Adresse valeur
i 2293572 3
p 2293568 2293572

101
3 Notion de pointeur
— L'opérateur * permet d'accéder à la valeur de l'objet pointé.
— Si p est un pointeur vers un entier i, *p désigne la valeur de i.
— Exemple:
main(){
int i =  3;
int *p;
*p = 3
p  =  &i;
printf("*p  =  %d  \n",  *p);
}

objet adresse valeur


i 2293572 3
p 2293568 2293572
*p 2293572 3

Cela signifie en particulier que toute modification de *p modifie i.


102
3 Notion de pointeur
— Exemple:
main(){
int i =  3;
int *p;
p  =  &i;
printf("Avant :  i =  %d  et  *p  =  %d  \n",  i,  *p);
*p  =  7;
printf("Apres :  i =  %d  et  *p  =  %d  \n",  i,  *p);
}

Avant : i = 3 et *p = 3
Apres : i = 7 et *p = 7

103
3 Notion de pointeur
— On peut donc dans un programme manipuler à la fois les
objets p et *p.
è Ces deux manipulations sont très différentes.
— Comparons les deux programmes suivants :
(Programme 1) (Programme 2)
main(){ main(){
int i =  3,  j  =  6; int i =  3,  j  =  6;
int *p1,  *p2; int *p1,  *p2;
p1  =  &i;             p1  =  &i;            
p2  =  &j; p2  =  &j;
*p1  =  *p2; p1  =  p2;
} }

104
3 Notion de pointeur
— Configuration des deux programmes avant la dernière affectation :

Objet adresse valeur


i 2293572 3
j 2293568 6
p1 2293564 2293572
p2 2293560 2293568

Après *p1 = *p2 Après p1 = p2

Objet adresse valeur Objet adresse valeur


i 2293572 6 i 2293572 3
j 2293568 6 j 2293568 6
p1 2293564 2293572 p1 2293564 2293568
p2 2293560 2293568 p2 2293560 2293568
105
4 Arithmétiques des pointeurs
— La valeur d'un pointeur étant un entier :
è on peut lui appliquer un certain nombre d'opérateurs
arithmétiques sur un pointeur.
— Les seules opérations valides sont :
— addition d'un entier à un pointeur :
— Résultat = pointeur de même type que le pointeur de départ ;
— soustraction d'un entier à un pointeur :
— Résultat = pointeur de même type que le pointeur de départ ;
— différence de deux pointeurs pointant tous deux vers
des objets de même type :
— Résultat est un entier.

Notons que la somme de deux pointeurs n'est pas autorisée.


106
4 Arithmétiques des pointeurs
— Si i est un entier et p est un pointeur sur un objet de type
Type : p + i désigne un pointeur sur un objet de type Type
dont la valeur est égale à la valeur de p incrémentée de i *
sizeof(type).
— Il en va de même pour la soustraction d'un entier à un
pointeur, et pour les opérateurs d'incrémentation et de
décrémentation ++ et --.

107
4 Arithmétiques des pointeurs
— Exemple :
main(){
int i =  3;
int *p1,  *p2;
p1 = 2293572 p2 = 2293576
p1  =  &i;
p2  =  p1  +  1;
printf("p1  =  %ld \t  p2  =  %ld\n",  p1,  p2);
}

main(){
double i =  3; p1 = 2293568 p2 = 2293576
double *p1,  *p2;
p1  =  &i;
p2  =  p1  +  1;
printf("p1  =  %ld \t  p2  =  %ld\n",  p1,  p2);
}

Les opérateurs de comparaison sont également applicables aux pointeurs.


108 à condition de comparer des pointeurs qui pointent vers des objets de même type.
4 Arithmétiques des pointeurs
— L'utilisation des opérations arithmétiques sur les pointeurs est
particulièrement utile pour parcourir des tableaux.
— Exemple:
#define N  5
int tab[5]  =  {1,  2,  6,  0,  7};
main(){
int *p;
printf("Ordre  croissant:  ");
for (p  =  &tab[0];  p  <=  &tab[N-­‐1];  p++)
printf("%d  ",*p);
printf("\nOrdre decroissant:  ");
for (p  =  &tab[N-­‐1];  p  >=  &tab[0];  p-­‐-­‐)
printf("%d  ",*p);  
} Ordre  croissant:  1  2  6  0  7
Ordre  decroissant:  7  0  6  2  1

Si p et q sont deux pointeurs sur des objets de type Type, l'expression


p - q désigne un entier dont la valeur est égale à (p - q)/sizeof(type).
109
5 Allocation dynamique
— Avant de manipuler un pointeur, il faut l'initialiser.
— Sinon, par défaut, la valeur du pointeur est égale à une
constante symbolique notée NULL définie dans stdio.h.
— En général, cette constante vaut 0.
— Le test p == NULL permet de savoir si le pointeur p pointe
vers un objet.
— On peut initialiser un pointeur p par une affectation sur p.

p  =  &a;

110
5 Allocation dynamique
— Il est également possible d'affecter directement une valeur à
*p, mais pour cela, il faut d'abord réserver à *p un espace-
mémoire de taille adéquate.
— L’allocation de la mémoire en C se fait par la fonction malloc
de la librairie standard stdlib.h. dont le prototype est :
void *malloc(size_t size);

— Le seul paramètre à passer à malloc est le nombre d'octets à


allouer.
— La valeur retournée est l'adresse du premier octet de la zone
mémoire alloué.
— Si l'allocation n'a pu se réaliser (par manque de mémoire libre),
la valeur de retour est la constante NULL.
111
5 Allocation dynamique
— Pour initialiser un pointeur vers un entier, on écrit :
#include <stdlib.h>
main(){
int *p;
p  =  (int*)malloc(sizeof(int));
}

— On aurait pu écrire également


p  =  (int*)malloc(4);
— puisqu'un objet de type int est stocké sur 4 octets.
— Mais on préférera la première écriture qui a l'avantage d'être
portable.

112
5 Allocation dynamique
— Le programme suivant définit un pointeur p sur un objet *p
de type int, et affecte à *p la valeur de la variable i :
#include <stdio.h>
#include <stdlib.h>
main(){
int i  =  3;
int *p;
printf("valeur  de  p  avant  initialisation  =  %d\n",  p);
p  =  (int*)malloc(sizeof(int));
printf("valeur  de  p  apres initialisation  =  %d\n",  p);
*p  =  i;
printf("valeur  de  *p  =  %d\n",*p);
}

valeur  de  p  avant  initialisation  =  2293576


valeur  de  p  apres initialisation  =  5508960
valeur  de  *p  =  3
113
5 Allocation dynamique
— Lorsque l'on n'a plus besoin de l'espace-mémoire alloué
dynamiquement, il faut libérer cette place en mémoire. Ceci
se fait à l'aide de la fonction free dont le prototype est :
void free(void *ptr);

114
6 Pointeurs et tableaux
— L'usage des pointeurs en C est, en grande partie, orienté vers
la manipulation des tableaux.
— Tout tableau en C est en fait un pointeur constant.
— Dans la déclaration :

int tab[10];

tab est un pointeur constant (non modifiable) dont la valeur


est l'adresse du premier élément du tableau.
Autrement dit, tab a pour valeur &tab[0].

115
6 Pointeurs et tableaux
— On peut donc utiliser un pointeur initialisé à tab pour
parcourir les éléments du tableau.
#define N  5
int tab[5]  =  {1,  2,  6,  0,  7};
main(){
int i;
int *p;
p  =  tab;
for (i =  0;  i <  N;  i++){
printf("%d\t",p[i]);
}
}

1              2              6              0              7
116
6 Pointeurs et tableaux
— On accède à l'élément d'indice i du tableau tab grâce à
l'opérateur d'indexation [], par l'expression tab[i].
— Cet opérateur d'indexation peut en fait s'appliquer à tout
objet p de type pointeur.
— Il est lié à l'opérateur d'indirection * par la formule :
p[i]  ==  *(p  +  i)

— Résultat: Pointeurs et tableaux se manipulent donc


exactement de même manière.

117
6 Pointeurs et tableaux
— Exemple:
#define N  5
int tab[5]  =  {1,  2,  6,  0,  7};
main(){
int i;
int *p;
p  =  tab;
for  (i =  0;  i <  N;  i++)
printf("%d\t",  *(p+i));
}

1              2              6              0              7

118
6 Pointeurs et tableaux
— Un tableau à 2D est, par définition, un tableau de tableaux.
èIl s'agit donc en fait d'un pointeur vers un pointeur.
— Considérons le tableau à deux dimensions défini par :

int tab[M][N];

— tab est un pointeur, qui pointe vers un objet lui-même de type


pointeur d'entier et a une valeur constante égale à l'adresse du
premier élément du tableau, &tab[0][0].
— De même tab[i] (pour i entre 0 et M-1) est un pointeur constant
vers un objet de type entier, qui est le premier élément de la ligne
d'indice i. tab[i] a donc une valeur constante qui est égale à
&tab[i][0].

119
6 Pointeurs et tableaux

120
6 Pointeurs et tableaux
— On déclare un pointeur qui pointe sur un objet de type Type *
(deux dimensions) de la même manière qu'un pointeur:

Type **  nomPointeur;

— De même un pointeur qui pointe sur un objet de type Type **


(équivalent à un tableau à 3 dimensions) se déclare par :

Type ***  nomPointeur;

121
6 Pointeurs et tableaux
— Exemple:
main(){
int k,  n;
int **tab;  
tab  =  (int**)malloc(k  *  sizeof(int*));

for (i =  0;  i <  k;  i++)


tab[i]  =  (int*)malloc(n  *  sizeof(int));
...
for (i =  0;  i <  k;  i++)
free(tab[i]);
free(tab);
}

122
7 Pointeurs et structures
— Contrairement aux tableaux, les objets de type structure
en C sont des Lvalues.
— Ils possèdent une adresse, correspondant à l'adresse du
premier élément du premier membre de la structure.
— On peut donc manipuler des pointeurs sur des structures.

123
7 Pointeurs et structures
— Exemple:
struct Eleve {
char nom[20];
float note;
};
typedef struct Eleve Eleve;
typedef Eleve *  Classe;
main(){
int n,  i;
Classe TE;
printf("Nombre  d'eleves de  la  classe  :  ");                scanf("%d",&n);
TE  =  (Classe)malloc(n  *  sizeof(Eleve));
for (i =0  ;  i <  n;  i++){
printf("\nSaisie de  l'eleve numero :  %d\n",i);
printf("\tNom :  ");        scanf("%s",  TE[i].nom);
printf("\tNote :  ");        scanf("%f",&TE[i].note);
}
printf("\nEntrez un  numero :  ");      scanf("%d",&i);
printf("\nEleve numero %d  est :  ",  i);
printf("\nNom ===>  %s",TE[i].nom);
printf("\nNote ===>  %.2f\n",TE[i].note);
free(TE);
124
}
7 Pointeurs et structures
— Si p est un pointeur sur une structure, on peut accéder à un
membre de la structure pointé par l'expression :
p-­‐>membre

Ou

(*p).membre

125
7 Pointeurs et structures
— Exemple:
struct Eleve {
char nom[20];
float note;
};
typedef struct Eleve Eleve;
main(){
Eleve *  pE;
pE =  (Eleve*)malloc(sizeof(Eleve));
strcpy(pE-­‐>nom,  "Alami");
pE-­‐>note =  13;  
printf("L'eleve %s  a  %.2f/20\n",  (*pE).nom,  (*pE).note);
free(pE);
}

L'eleve Alami  a  13.00/20

126

Vous aimerez peut-être aussi