Académique Documents
Professionnel Documents
Culture Documents
L a Mémoire Centrale (MC) est formée par des cases numérotées. Chaque case peut stocker un ou
plusieurs octets. Une variable est une zone contigüe en MC (une case ou un ensemble de cases
qui se suivent). Sa taille (en nombre d’octets) dépend du type de la variable (ex: un entier
occupe 4 octets, un réel occupe 8 octets, ...etc). L'adresse d'une variable est le numéro de sa première
case.
En revanche les variables dynamiques ne peuvent être adressées directement par un identificateur.
Elles sont accessibles par l’intermédiaire d’une variable statique spéciale (dite « pointeur ») contenant
l’adresse mémoire de la variable dynamique. On dit que le pointeur pointe vers cette variable
dynamique ou que cette variable dynamique est pointée par le pointeur.
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
Exemple
Type
personne = record
Nss: integer; /* 4 Octets
nom: string[25]; /* 25 Octets
prenom: string[25]; /* 25 Octets
end;
Var
Liste : array [1..500] of personne; /* 500x[4+20+20]=27000
octets = 27KO
Ce tableau à 500 éléments est réservé tout au long de l’exécution du programme ; c.-à-d., l’espace
occupé ne sera libéré qu’à la fin de l’exécution du programme.
Si on veut passer ce tableau en paramètre à une procédure, le compilateur vous renverrait une
erreur du type : Structure too large car il lui est impossible de réserver plus de 16 Ko en
mémoire pour les variables des sous-programmes. D'où l'intérêt des pointeurs car quel que
soit la grosseur de la variable pointée, la place en mémoire du pointeur est toujours la même :
4 octets (8 octets pour systèmes 64 bits).
La déclaration d'un pointeur permet donc de réserver une petite place de la mémoire qui
pointe vers une autre qui peut être très volumineuse. L'intérêt des pointeurs est que la variable
pointée ne se voit pas réserver de mémoire, ce qui représente une importante économie de
mémoire permettant de manipuler un très grand nombre de données. Puisque la Pile
normalement destinée aux variables des sous-programmes est trop petite (16 Ko), on utilise
donc le Tas réservé au pointeur qui nous laisse jusqu'à 64 Ko, soit quatre fois plus !
Avant d'utiliser une variable de type pointeur, il faut déclarer ce type en fonction du type de
variable que l'on souhaite pointer.
Définition
Un pointeur est une variable qui contient l’adresse d’une donnée contenue en mémoire (autre variable
stockée en mémoire). La déclaration d’une variable pointeur réserve 4 octets nécessaires au codage
de l’adresse mémoire de la donnée pointée mais ne réserve aucune mémoire pour la donnée pointée
Quel que soit le type de la donnée pointée, la taille mémoire du pointeur est toujours la même : 4
octets.
2|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
Exemple 1 :
Var P : ^Integer;
On déclare une variable P de type ^Integer qui est en fait un pointeur pointant vers un
Integer (à noter la présence indispensable de l'accent circonflexe!). Donc la variable
(pointeur) P contient une adresse mémoire, celle d'une autre variable qui est-elle, de type
Integer. Cette déclaration n’effectue pas de réservation Elle réserve bien un emplacement
nommé P mais uniquement pour pouvoir y placer plus tard une adresse : celle de la valeur pointée.
Ainsi l'adresse mémoire contenue dans P est l'endroit où se trouve le premier octet de la
variable de type Integer. Il est inutile de préciser l'adresse mémoire de fin de
l'emplacement de la variable de type Integer car une variable de type connu quel que soit
sa valeur occupe toujours le même espace. Le compilateur sachant à l'avance combien de
place tient tel ou tel type de variable, il lui suffit de connaître grâce au pointeur l'adresse
mémoire du premier octet occupé et de faire l'addition adresse mémoire contenue dans le
pointeur + taille mémoire du type utilisé pour définir totalement l'emplacement mémoire de la
variable pointée par le pointeur.
Tout ça c'est très bien mais comment fait-on pour accéder au contenu de la variable pointée
par le pointeur ? Il suffit de manipuler l'identificateur du pointeur à la fin duquel on rajoute un
accent circonflexe en guise de variable pointée.
Exemple :
P^ := 128 ;
Donc comprenons-nous bien, P est le pointeur contenant l'adresse mémoire d'une variable et
P^ (avec l'accent circonflexe) contient la valeur de la variable pointée. On passe donc du
pointeur à la variable pointée par l'ajout du symbole spécifique ^ à l'identificateur du
pointeur.
P P^
P^ : permet d’accéder à la variable pointée par P ;
3|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
Exemple
Var
ptr1 : ^Integer ;
ptr2 : ^Integer ;
Begin
New (ptr1);
ptr1^:=2;
ptr2:=ptr1;
End.
Par rapport à nos explications sur le segment de données, le pointeur lui-même est alloué dans la partie statique
du segment de données tandis-que l’emplacement pointé est alloué sur le tas.
P ?
1.4.COMPARAISON ET AFFECTATION
Un pointeur d’un type T peut être comparé avec tout autre pointeur de type T ou avec la valeur nil. Il
en va de même pour l’affectation.
Exemple
Program pointeurs2;
Type
T_Pointeur_Entiers = ^integer;
Var Quelles sont les 2 valeurs affichées
Ptr_Int1, Ptr_Int2 : T_Pointeur_Entiers; pour Ptr_Int2^ ?
Begin
New(Ptr_Int1);
Ptr_Int1^ := 10;
Ptr_Int2 := Ptr_Int1;
Writeln('Ptr_Int2 pointe sur ', Ptr_Int2^);
Dispose(Ptr_Int1);
Writeln('Ptr_Int2 pointe sur ', Ptr_Int2^);
End.
4|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
d’indirection : placé devant un nom de variable, il renvoie l’adresse mémoire de celle-ci, qui peut donc
être affectée à un pointeur. La forme générale est : ptr := @variable ;
Les pointeurs sont typés ce qui signifie qu’ils ne peuvent recevoir que des adresses d’emplacements du type pour
lequel ils ont été définis. On ne peut affecter une adresse de réel à un pointeur d’entier, par exemple.
Exemple
Program POINTEURS_1;
Var
Ent_1, ent_2 : INTEGER;
ptr_ent_1, ptr_ent_2 : ^INTEGER;
Begin
ent_1 := 10; ent_2 := 100;
ptr_ent_1 := @ent_1;
ptr_ent_2 := @ent_2; Après l’exécution du
writeln('ptr_ent_1 pointe sur ', ptr_ent_1^); programme on aura
writeln('ptr_ent_2 pointe sur ', ptr_ent_2^);
ptr_ent_1 := ptr_ent_2;
writeln('ptr_ent_1 pointe sur ', ptr_ent_1^);
writeln('ptr_ent_2 pointe sur ', ptr_ent_2^);
ptr_ent_1^ := 2000;
writeln('entier_1 = ', ent_1,'ent_2 = ', ent_2);
ptr_ent_1^ := ptr_ent_1^ + ent_1;
writeln('entier_2 = ', ent_2);
ent_2 := ptr_ent_2^ * 10;
writeln('entier_2 = ', ent_2);
End.
Var
p : ^T_tab; {allocation statique de la variable p (un pointeur)}
i : integer; {allocation statique de la variable i (un entier)}
Begin
New(p);{un tableau de 100 entiers vient d'être alloué dynamiquement, son adr est dans p }
p^[1] := 10;
p^[2] := 3;
p^[3] := p^[1] * 2 + 5;
dispose( p );{ maintenant le tableau n'existe plus, on a plus le droit de manipuler p^ }
{mais on peut créer un autre tableau on utilisant le même pointeur}
new(p);
For i:=1 to 100 do
p^[i] := i*i;
End.
5|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
T_Personne n’est connu qu’après le end; de la définition : Pascal ne connaît donc pas la taille mémoire qu’il
faut réserver pour les champs père et mère. Les pointeurs, et la possibilité d’anticiper la définition d’un type
permettent de corriger ce problème :
Type
T_Ptr_Personne = ^T_Personne;
T_Personne = record
nom : string;
pere, mere : T_Ptr_Personne; {pointeur}
End;
C’est cette méthode que l’on utilisera pour toutes les structures de données gérée dynamiquement,
telles que les listes.
2. LISTES CHAINEES
Les listes sont des structures de données informatiques qui permettent, au même titre que les tableaux
par exemple, de garder en mémoire des données en respectant un certain ordre : on peut ajouter,
enlever ou consulter un élément en début ou en fin de liste, vider une liste ou savoir si elle contient un
ou plusieurs éléments.
6|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
C’est l’implémentation la plus naturelle et la plus utilisée. Dans cette représentation, chaque élément
de la liste est stocké dans une cellule et les cellules sont reliées par des pointeurs. Chaque cellule
contient, en plus de la donnée à traiter, l’adresse de la cellule suivante. A chaque cellule est associée
une adresse mémoire. Les Listes Linéaires Chaînées (LLC) font appel à la notion de variable
dynamique. Cette dernière peut y être créée ou détruite (c’est-à- dire, on lui alloue un espace mémoire
à occuper et qu’il sera libéré après son utilisation).
2.3.DEFINITION (RECURSIVE)
En considérant que la liste la plus simple est la liste vide (notée [ ]), qui ne contient aucun élément, on
peut donner une définition récursive aux listes chaînées d’éléments de type T :
La liste vide [ ] est une liste ;
Si e est un élément de type T et L est une liste d’éléments de type T, alors le couple (e, L) est
aussi une liste, qui a comme premier élément e et dont le reste des éléments (à partir du
second) forment la liste L.
Cette définition est récursive, car une liste est définie en fonction d’une autre liste. Une telle définition
récursive est correcte, car une liste est définie en fonction d’une liste plus courte, qui contient un
élément de moins. Cette définition permet de construire n’importe quelle liste en partant de la liste
vide.
Conclusion : la liste chaînée est une structure de données récursive.
Une LLC est caractérisée par l’adresse de son premier élément tête(L), (La liste donne
accès à la première cellule, le reste de la liste est accessible en passant de cellule en cellule,
suivant leur enchaînement).
Nil constitue l’adresse qui ne pointe sur aucune cellule. Elle est utilisée pour marquer la fin de
la liste,
Une LLC vide est une liste qui ne contient aucun élément, elle est représentée par Nil,
Si la place P est référencée par le pointeur P dans L, le suivant de P sera référencé par
P^.suivant. Dans l’exemple, si P occupe la position 2 (la cellule qui contient la valeur18),
P^.suivant pointe sur la cellule 3 (la cellule qui contient la valeur 12),
7|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
Pour accéder à la valeur stockée dans la cellule C de la liste L, il suffit d’écrire C^.valeur.
Dans l’exemple, si C représente la cellule 2, alors C^.valeur donne la valeur 18.
Une liste chaînée est une collection d’éléments possédant chacun deux composantes : un pointeur vers le prochain élément
de la liste et une « valeur » de n’importe quel type.
Lors de la création, attention à bien initialiser la tête de la liste à Nil, sinon la chaine n’aura pas de buttoir final et il ne sera
pas possible de repérer sa fin. Une LLC est connue par l’adresse de son premier élément. Si cette adresse n’est pas
mémorisée, la liste disparait. Donc, il faut toujours avoir l’adresse de la tête mémorisée.
8|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
Begin
| L := Nil ; /* Pour l’instant la liste est vide*/
| New(P) ; /* Réserver un espace mémoire pour le premier élément */
| Read (P^.info) ; /* Stocker dans l’Info de l’élément pointé par P la valeur saisie */
| P^.suivant := Nil ; /* Il n’y a pas d’élément suivant */
| L := P; /* Le pointeur Tete pointe maintenant sur P */
| Repeat
| | New(Q) ; /* Réserver un espace mémoire pour l’élément suivant */
| | Read (Q^.info) ; /* Stocker dans l’Info de l’élément pointé par Q la valeur saisie */
| | Q^.suivant :=Nil ; /* Mettre Nil dans la partie adresse du dernier élément*/
| | P^.suivant := Q; /* Relier l’élément précédant avec l’élément suivant */
| | P :=Q; /* Le pointeur P pointe sur Q*/
| | Writeln (‘Voulez-vous créer un autre élément’) ;
| | Read (reponse) ;
| Until (reponse =’NON’);
End.
9|Page
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
10 | P a g e
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
Program Ajout_fin ;
Type liste =^cellule
cellule = Record
info : integer ;
suivant : liste ;
End;
Var L, nouveau, P : liste ;
Begin
| New(nouveau ) ; /* Réserver un espace mémoire pour le nouveau élément*/
| read(nouveau^.info) ; /* Stocker dans l’Info de l’élément pointé par nouveau la valeur saisie*/
| nouveau^.suivant := Nil ; /*Mettre Nil dans le suivant de nouveau,car, il sera le dernier élément de L.*/
| P :=L ; /* Mémoriser la tête de L dans une autre liste pour ne pas la perdre */
|while( P^.suivant < > Nil) do /* Parcourir L jusqu’à le dernier élément*/
| | P:= P^.suivant ; /* Passer d’un élément à l’autre*/
|
| P^.suivant:=nouveau ; /* Relier le dernier élément de L avec nouveau */
End.
Program supprimer_premier ;
Type liste =^cellule ;
cellule = Record
info : integer;
suivant : liste ;
End;
Var L, P : liste ;
Begin
| If (L<>Nil) alors /* La liste n’est pas vide on peut donc supprimer le premier élément*/
| Begin
| | P := L ; /* P pointe sur le premier élément de L*/
| | L := L^.suivant ; /* L pointe sur le deuxième élément */
| | Dispose (P) ; /* Libération de l’espace mémoire qu’occupait le premier élément */
| End
| Else
| | Write("la liste L est vide") ;
|
End.
11 | P a g e
Chapitre 8 : LES POINTEURS & LISTES CHAINEES
Program supprimer_milieu ;
Type liste =^cellule
cellule = Record
info : integer;
suivant : liste ;
End ;
var L, P, precedent : liste ;
Begin
if (L <> Nil) Then /* La liste n’est pas vide on peut donc supprimer le premier élément */
| | Precedent := L ; /*Pointeur précédent */
| | P := L^.suivant ; /* Pointeur courant */
| | while( l’élément à supprimer n’est pas encore trouvé) do /* Localisation du l’élément à supprimer*/
| | | Precedent := P; /*On garde la position du précédent */
| | | P:= P^.suivant ; /* On passe à l’élément suivant dans la liste */
| | end;
| | Precedent^.suivant := P^.suivant ; /*On "saute" l’élément à supprimer */
| | Dispose(P); /*Libération de l’espace mémoire qu’occupait l’élément P*/
else
| | write ("la liste L est vide") ;
End.
Program supprimer_dernier ;
Type liste =^cellule
cellule = record
info : integer;
suivant : liste;
End;
Var L, P, precedent : liste ;
Begin
| if (L <> Nil) then /* La liste n’est pas vide on peut donc supprimer le premier élément */
| | Precedent :=L ; /*Pointeur précédent */
| | P :=L^.suivant ; /* Pointeur courant */
| | while (P^.suivant <> Nil) do/* Localisation du dernier élément*/
| | | Precedent := P; /*On garde la position du précédent */
| | | P:= P^.suivant ; /* On passe à l’élément suivant dans la liste */
| | end;
| | Dispose(P) ; /*Libération de l’espace mémoire qu’occupait l’élément P*/
| | Precedent^.suivant:= Nil /* Mettre Nil dans la partie suivant du précédent */
| else
| | write ("la liste L est vide") ;
End.
12 | P a g e