Chapitre 02
Chapitre 02
Chapitre 02
1. Introduction :
Les structures de données statiques (les tableaux) ont l’inconvénient du gaspillage de la
mémoire et parfois d’insuffisance de la mémoire. La solution c’est les structures de données
dynamiques ou l’espace mémoire est alloué au fur et à mesure par l’algorithme. Les cellules de
la mémoire centrale (RAM) sont regroupées en octet et chaque octet est numéroté par un
numéro unique appelé adresse mémoire. Les adresses mémoire sont stockées dans des variables
de type pointeur.
2. Le type pointeur
Le type pointeur est destiné pour contenir une adresse mémoire. Une variable de type pointeur
est stockée dans une case mémoire et contient l’adresse d’une autre case mémoire.
M.C
@001
P = @004 @002
@003
@004
@005
@006
@007
2.1. Déclaration
Pour déclarer une variable de type pointeur, il faut préciser le type de la case mémoire à pointer. La
syntaxe pour déclarer un pointeur est la suivante
syntaxe
Algorithmique Langage C
Var id_pointeur : ↑type; type *id_pointeur;
Exemples
Var P : ↑entier; int *P;
Type pointeur = ↑Etudiant; typedef struct Etudiant Etudiant;
Etudiant = enregistrement Etudiant *q ;
Nom: chaine[30]; struct Etudiant{
Prenom : chaine[30]; char Nom[30];
Moyenne : reel; char Prenom[30];
Var q : pointeur; float Moyenne ;};
Dans l’exemple ci-dessus, P est un pointeur destiné à contenir une adresse mémoire d’une case de type
entier et q est destiné à contenir l’adresse d’une variable de type Etudiant. Il est possible de déclarer
un pointeur vers un type qui n’est pas encore défini, car la taille d’une case mémoire de type pointeur
est la même quel que soit le type pointé (un pointeur est codé généralement sur 4 octets).
Les variables de type pointeur sont appelées ‘variables dynamiques’ l’espace mémoire nécessaire est
réservé dans la partie action de l’algorithme. C’est au programmeur de gérer les variables dynamiques
en utilisant deux opérations prédéfinies :
Allouer(P) : permet de réserver une case mémoire est sans adresse est stocker dans le pointeur
P fourni en paramètre.
Liberer(P) : permet de libérer la case mémoire pointé par le pointeur P et sa valeur devient
NIL
L’espace mémoire réservé avec la procédure Allouer reste occupé même après terminaison du
programme. Dans un algorithme il faut libérer tous les objets pointeurs en utilisant la procédure
liberer() pour ne pas saturer la mémoire de la machine.
Après déclaration d’une variable de type pointeur, celle-ci ne contient aucune adresse et elle ne pointe
vers aucune case mémoire. Sa valeur est la valeur par défaut ‘NIL’ et signifie que le pointeur ne pointe
nul part. Pour réserver la case mémoire d’un pointeur il faut utiliser la procédure prédéfinie Allouer(P).
Pour accéder et manipuler le contenue de la case mémoire pointée par P en écrit P↑
Remarque : la valeur ‘NIL’ d’un pointeur est très importante. Le test Si (P = NIL) permet de savoir
si le pointeur contient une adresse.
Exemples :
Var p : ↑entier;
allouer(p);
p↑ 25 ;
L’instruction allouer(p); permet de réserver un espace mémoire pour un entier et son adresse est
stockée dans le pointeur p. La case mémoire pointé par le pointeur p est désignée par p↑.
Algorithme pointeur ;
Type Etudiant = enregistrement;
Nom: chaine[30];
Prenom : chaine[30];
Moyenne : reel;
Var q : ↑Etudiant;
Debut
| Allouer(q);
| Avec (q↑) faire
| | Lire(nom,prenom,moyenne);
| finAvec;
| ecrire(q↑.nom, q↑.prenom);
Fin.
L'opérateur unaire d'indirection * permet d'accéder directement à la valeur de l'objet pointé. Ainsi,
si p est un pointeur vers un entier i, *p désigne la valeur de i.
int main()
{ int i = 3;
int *p;
p = &i;
printf("*p = %d \n",*p);
}
L’opérateur & renvoie l’adresse mémoire d’une variable, le pointeur p contient l’adresse de i. Alors, le
programme affiche *p = 3.
Les opérations possibles sur un pointeur sont :
L’addition d’un entier à un pointeur.
La soustraction d’un entier d’un pointeur
Le type des pointeurs est important pour leur arithmétique. Si i est un entier et p est un pointeur sur un
objet de type type, l'expression 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.
Exemples :
main()
{
int i = 3;
int *p1, *p2;
p1 = &i;
p2 = p1 + 1;
printf("p1 = %ld \t p2 = %ld\n",p1,p2);
}
Le programme affiche p1 = 4831835984 p2 = 4831835988.
Par contre, le même programme avec des pointeurs sur des objets de type double :
main()
{
double i = 3;
double *p1, *p2;
p1 = &i;
p2 = p1 + 1;
printf("p1 = %ld \t p2 = %ld\n",p1,p2);
}
Affiche p1 = 4831835984 p2 = 4831835992.
Le pointeur L contenant l’adresse du premier enregistrement est appelé tête liste. Le dernier
enregistrement d’une liste chainée ne contient aucune adresse.
Dans la suite de ce document les opérations élémentaires sur une liste chainée
sont implémentées à savoir :
La création
La suppression d’un élément.
La suppression d’une liste (vider la liste).
Affichage les éléments de la liste.
3.2. Déclaration
Un élément d’une liste chainé est un enregistrement contenant des champs pour des
informations à traiter et un champ de type pointeur pour stocker l’adresse de l’élément suivant
dans la liste.
syntaxe
Algorithmique Langage C
Type nom_element = enregistrement struct nom_element {
info: type ; type info;
suivant:↑nom_element; struct nom_element *suivant;
Fin ; };
Exemples
Type liste : ↑Etudiant ; typedef struct Etudiant Etudiant;
Etudiant = enregistrement typedef Etudiant* liste ;
Nom: chaine[30]; struct Etudiant{
Prenom : chaine[30]; char Nom[30];
Moyenne : reel; char Prenom[30];
Suivant : liste ; float Moyenne ;
Var L: liste; liste suivant;
};
Liste L ;
Dans ce qui suit, une liste de nombre entier est utilisée pour la définition des procédures
permettant de réaliser les opérations de base. Considérons la structure de donné pour
implémenter une liste de nombre entiers.
Type liste : ↑Element;
Element = enregistrement
Val: entier;
Suivant : liste ;
Fin ;
3.3. Création d’une liste chainée
Lors de la déclaration d’une variable de type liste, sa valeur est NIL. Initialement une liste ne
contient aucune adresse mémoire, les enregistrements sont créés et ajoutés à la liste par
l’algorithme. La création d’une liste peut se faire par ajout en fin de liste ou au début de la
liste.
Pour ajouter un élément à une liste chainée, il faut d’abord allouer un espace mémoire pour
l’enregistrement et initialiser les champs information
allouer(p);
lire(p↑.Val);
(1) p↑.suivant ← L;
(2) L ← p;
Ci-dessous la procédure permettant de créer une liste d’entier avec ajout en début de liste.
Procedure Creer_debut(var L: liste) ;
Var p :liste;
rep :caractere ;
Debut
Repeter
Allouer(p); // création du nouvel élément
Lire(p↑.val); // lire la valeur de l’élément
p↑.suivant←L;// attacher le nouvel enregistrement à la liste
L←p ; // modifier la tête de la liste
Ecrire('voulez vous continuer o/n') ;
Lire(rep) ;
Jusqua(rep='n');
Fin.
q↑.suivant ← p;
Ci-dessous la procédure permettant de créer une liste d’entier avec ajout en fin de liste.
Procedure Creer_fin(var L: liste) ;
Var p,q :liste;
rep :caractere ;
Debut
Allouer(L); // création du premier élément
Lire(L↑.val);
q←L; // positionner le pointeur q sur la fin de la liste
Ecrire('voulez vous continuer o/n') ;
Lire(rep) ;
Tantque(rep = 'o')
Allouer(p); // création du nouvel élément
Lire(p↑.val); // lire la valeur de l’élément
q↑.suivant←p;// attacher le nouvel enregistrement à la liste
q←p ; // déplacer le pointeur q en avant pour pointer le dernier
élément. le pointeur q doit pointer toujours le dernier élément.
Ecrire('voulez vous continuer o/n') ;
Lire(rep) ;
finTQ;
Fin.
Si l’élément à supprimer n’est pas le premier de la liste, il faut positionner un pointeur p sur
l’élément à supprimer et un autre pointeur q sur l’élément le précédant et procéder comme
suit :
1. q↑.suivant ← p↑.suivant ; // modifier les liens de chainage
2. liberer(p) ; // suppression
La procédure supprimer ci-dessous permet de supprimer une valeur v d’une liste d’entier L
(on suppose qu’il y’a une seule occurrence de la valeur v dans la liste L).
Procedure supprimer(var L: liste, v:entier) ;
Var p,q :liste;
Debut
Si(L↑.Val=v) alors
p←L;
L ←L↑.suivant ;
Liberer(p);
Sinon
q←L;
p←L↑.suivant ;
Tantque(p↑.val <> v)&&(p<> NIL)
q←p ;
p←p↑.suivant;
finTQ;
Si(p<>NIL)Alors
q↑.suivant ← p↑.suivant ;
liberer(p) ;
finsi;
finsi;
Fin;
Les éléments d’une liste chainée ne sont pas indexés comme les éléments d’un tableau. Pour
parcourir une liste chainée on procède comme suit :