Pile
Pile
Pile
Objectifs
• Connaître la structure de données Pile
• Différencier entre la représentation contiguë et la représentation chaînée de la SD Pile
• Savoir les opérations élémentaires et élaborées appliquées sur la Pile
• Implémenter en C les différents services de la pile.
• Concrétiser Pile TDA et Pile OA
• Utiliser les services de la SD Pile
Plan
1. Définition
2. Propriétés
3. Représentation physique d’une Structure de donnée
3.1. Représentation chaînée de la SD pile
3.2. Représentation contiguë
3.3. Évaluation entre représentation chaînée et contiguë
4. Matérialisation de la SD Pile
4.1. Structure de données Pile comme Objet Abstrait
4.2. Structure de données Pile comme type de donnée abstrait
5. Évaluations
5.1. Position ou emplacement de la représentation physique de la SD
5.1.1. Utilisation OA (cas SD Pile)
5.1.2. Utilisation d’un type de données abstrait (cas SD Pile)
5.2. Paramétrage de la SD sur le type des éléments
5.3. Positions des assertions
6. Exercice d’application
1
1. Définition
On appelle pile un ensemble de données, de taille variable, éventuellement nul, sur lequel on
peut effectuer les opérations suivantes :
• creer_pile : créer une pile vide
• vide : tester si une pile est vide
• empiler : ajouter un élément de type T à la pile
• depiler : supprimer un élément de la pile
• dernier : retourner le dernier élément empilé et non encore dépilé.
Une structure de données (SD) Pile est composée de :
• Structure de mémorisation, elle stocke plusieurs éléments
• Opérations applicables sur cette SD
En générale, les opérations applicables sur une structure de donnée sont divisées en trois
catégories :
Les opérations de création (souvent une seule opération) : elles permettent de créer une
structure SD.
Illustration : SD pile : creer_pile
Les opérations de consultation : elles permettent de fournir des renseignements sur une SD
Illustration : SD pile : vide et dernier. Une opération de consultation laisse la SD
inchangée.
Les opérations de modification : elles agissent sur la SD.
Illustration : empiler et dépiler.
Opérations illégales :
Certaines opérations définies sur une SD exigent des pré-conditions : on ne peut pas
appliquer systématiquement ces opérations sur une SD.
Illustration :
dépiler exige que la pile soit non vide de même Idem pour l’opération dernier.
2. Propriétés
2
Remarque : Ces propriétés permettent de cerner la sémantique (comportement ou rôle) des
opérations applicables sur la SD pile.
En conclusion : la SD pile obéit à la loi LIFO (Last In, First Out) ou encore DRPS
(Dernier Rentré, Premier Sortie).
cle suivant
8
sommet 8 ox280
Ox10
4 Ox100 0
3 4 ox470
Ox28
0
Ox47 3
0
Traduction en C
Pour pouvoir regrouper les deux champs cle et suivant, on fait appel au type struct fourni
par C.
Hypothèse : les éléments sont de types entiers (int).
struct element
{
int cle;
struct element * suivant;
};
struct element * sommet ;
3
• Les éléments formant la pile sont créés grâce à empiler qui fait appel à malloc
(fonction fournie par C permettant d’allouer de l’espace mémoire).
• Les éléments de la pile peuvent être supprimés grâce à depiler qui fait appel à free
(fonction fournie par C permettant de libérer l’espace mémoire déjà alloué par
malloc).
• sommet est une variable de type pointeur.
sommet=NULL ; /*NULL est la valeur nulle du pointeur, il s’agit d’une constante
symbolique appartenant aux bibliothèques stdiod.h, stdlib.h, alloc.h*/
3.2. Représentation contiguë
Pour pouvoir représenter la structure de données Pile d’une façon contiguë, on fait appel à la
notion du tableau. Puisque le nombre d’éléments d’un tableau doit être fixé avant l’utilisation,
on aura deux parties :
• Partie utilisée : elle est comprise entre 0 et sommet.
• Partie non utilisé : elle est comprise entre sommet+1 et n-1.
8 sommet 3 0
utilisé
2 4 1
4
8 2
3
non utilisée
n-1
Traduction en C
solution 1 :
#define n 100
int cle [n] ;/*tableau pour mémoriser des entiers*/
int sommet ;
Critique : la solution précédente est mauvaise car elle dispose des éléments étroitement liés
(ici cle et sommet). D’où la solution suivante :
#define n 100
struct pile 1 2 3 4 5 n-1
{ cle
int cle[n]; →
int sommet;
};
struct pile p ;
sommet
4
3.3. Évaluation entre représentation chaînée et contiguë
• Quels sont les problèmes posés par la représentation contiguë ?
Estimation de n : trop grand ou trop petit
Cas n trop grand➔ perte mémoire
Cas n trop petit ➔ risque que des demandes d’adjonction (ajout) ne sont pas satisfaites (cas
d’empiler sur les SD pile). On risque d’avoir des empilements (opération empiler) non
satisfaits.
• Quels sont les problèmes posés par la représentation chaînée ?
Hypothèse : La pile contient à un instant donné n éléments
La représentation chaînée occupe plus de mémoire que la représentation contiguë ceci est
justifié par le coût mémoire de pointeurs présents dans la représentation chaînée.
Par exemple, pour N=100 et la taille d’un pointeur est deux octets :
• Représentation contiguë : espace occupé est de taille =100*sizeof(int)=100*2=200
octets
• Représentation chaînée : espace occupé=100*2+100*2=400octets
4. Matérialisation de la SD Pile
Objet abstrait(OA)
Matérialisation de la SD Pile :
Type de données Abstrait (TDA)
Objet abstrait (OA)➔ un seul exemplaire (ici une seule pile)→ unique → implicite.
Type de données Abstrait (TDA) ➔ plusieurs exemplaires→ il faut mentionner ou rendre
explicite l’exemplaire courant.
4.1. Structure de données Pile comme Objet Abstrait
L’interface pile regroupe des services exportés par cette interface. Chaque service correspond
à une opération applicable sur la SD (ici la SD Pile). Et il est fourni sous forme d’un sous-
programme.
Opérations applicables sur la SD Pile :
• Opération de création : procédure ou fonction
• Opération de modification : procédure
• Opération de consultation : fonction
✓ Interface Pile.h :
Elle comporte les déclarations des opérations applicables sur la SD pile
void ceer_pile(void) ;
/*opérations de consultation*/
unsigned vide (void) ; /* 1 si la pile est vide sinon 0*/
int dernier (void) ;
5
/*opérations de modification*/
void empiler (int) ; /* Le paramètre est la valeur à empiler*/
void depiler(void) ;
Remarque :
Les opérations définies dans pile.h agissent sur une pile unique et par conséquent implicite.
✓ Implémentation : pile.c
La partie implémentation (ici pile.c) réalise les services exportés par l’interface (ici pile.h).
Celle ci comporte :
• Les signatures ou les entêtes des services exportés
• Et pour chaque opération exportée par l’interface sa sémantique (rôle)
Ainsi, l’interface joue le rôle du guide d’implémentation vis à vis de la réalisation
#include <stdio.h>
#include <alloc.h>
#include <assert.h>
#include "pile.h" /* pile.h est censée être stocké dans le répertoire
courant appelé répertoire de travail*/
/*représentation physique*/
struct element
{
int cle ;
struct element * suivant ;
};
6
void depiler(void)
{
struct element*p ;
assert( !vide()) ;
p=sommet ;
sommet= sommet →suivant ;
free(p) ;
}
✓ Partie utilisation
La partie utilisation voit la SD (ici la SD pile) uniquement via son interface elle utilise les
services fournis par l’interface.
#include<stdio.h>
#include "pile.h"/*on compte utilise des services offerts par l’interface
pile.h*/
void main(void)
{
unsigned i;
creer_pile();
assert(vide());
for(i=1;i<=10;i++)
{
empiler(i);
}
assert(!vide());
for(i=1;i<=10;i++)
{
printf("%d\n",dernier());
dépiler();
}
assert(vide());
}
Idée
Tester les services exportés par l’interface "pile.h" (d’une façon indirecte pile.c).
Question :
Est-ce que la représentation physique est appropriée ou non ?
element
cle suivant
sommet
tt
→Passer par le calcul de la complexité des opérations applicables sur la SD pile matérialisé
par la représentation chaînée ci-dessus.
Conclusion : l’implémentation des opérations citées ci-dessus ne dépend pas des nombres
d’éléments stockés dans la pile.
7
4.2. Structure de données Pile comme type de donnée abstrait
Dans ce paragraphe, on va concrétiser la SD pile sous forme d’un TDA capable de gérer
plusieurs exemplaires de la SD pile et non pas un seul exemplaire (objet abstrait).
✓ Partie interface : pile.h :
/*représentation physique*/
struct element
{
int cle ;
struct element*suivant ;
} ;
8
✓ Partie utilisation : test.c
#include<stdio.h>
#include "pile.h"
void main()
{
struct element*p1;
struct element*p2;
unsigned i;
p1=creer_pile();
for(i=1;i<=10;i++)
{
empiler(i,&p1);
}
p2=creer_pile();
for(i=11;i<=20;i++)
{
empiler(i,&p2);
}
/*affichage de p1 et p2*/
for(i=1 ;i<=10 ;i++)
{
printf("%d\t%d\n",dernier(p1),dernier(p2));
depiler(&p1);
depiler(&p2);
}
/*en principe p1 et p2 sont vides*/
if(vide(p1)&&vide(p2))
printf("quelle joie!!");
else
printf("problème!?!?");
}
5. Évaluations
9
#include "pile.h"
....
....
void main(void)
{
struct element*p1;
p1=creer_pile();/*comme Pile*/
p1=NULL; /*pointeur*/
empiler(3,&p1); /*comme Pile*/
p1=(struct element*) malloc(size of(struct element)) ; /*pointeur*/
}
La représentation physique est dans la partie interface et par conséquent on peut manipuler
cette représentation lors de l’utilisation.
- Le langage de programmation C offre des moyens permettant la matérialisation d’une
SD sous forme d’un OA (de mettre la représentation physique dans la partie
implémentation en utilisant static).
- Par contre, le langage C présente des faiblesses vis-à-vis de la matérialisation de la SD
come TDA. En effet, il n’interdit pas la manipulation des aspects liés à la
représentation physique lors de l’utilisation.
5.2. Paramétrage de la SD sur le type des éléments
Une SD mémorise un ensemble d’éléments de même type ou de type conforme (l’héritage
dans le cadre de l'orienté objet). Les opérations applicables sur une SD ne dépendent pas de la
nature des éléments de cette SD.
pile.h
Illustration : SD Pile---->module
pile.c
10
6. Exercice d’application
Ecrire un programme en C permettant de lire un entier non signé et d’afficher tous les chiffres
qui composent le nombre.
✓ Exemple :
Si le nombre proposé est 2345 alors le programme souhaité doit afficher dans l’ordre :
2
3
4
5
✓ Solution
Idée : divisons entiers successives
=>utiliser la SD pile
2345 10
5 234 10
4 23 10
Génération 3 2 10
0
récupération
#include <stdio.h>
#include "pile.h"/*objet abstrait*/
void main(void)
{
unsigned nb ;
unsigned chiffre ;/*0…9*/
printf("nb=") ;
scanf("%u",&nb) ;
creer_pile() ;
do
{
chiffre=nb%10 ;/* reste de la division entière de nb par 10*/
empiler(chiffre) ;
nb=nb/10 ;
}while(nb!=0) ;
/*utilisation*/
while( !vide())
{
printf("%u\n",dernier()) ;
depiler() ;
}
}/*fin main*/
11
effacer : supprime tous les éléments d’une pile après avoir engagé effacer sur une pile
alors la pile résultante est vide. ➔ opération de modification
change_sommet : change l’élément au sommet de la pile. Elle exige que la pile soit
non vide ➔ opération de modification
L’intégration de nouvelles opérations applicables sur une SD exige :
- Modification apportée à l’interface
- Modification apportée à l’implémentation
unsigned nb_elements(void)
{
return nb ;
}
void creer_pile(void)
{
nb=0:
}
void empiler(int info)
{ nb++; }
void depiler(void)
{ nb--; }
void change_sommet(int nouvelle_valeur)
{
assert(!vide()) ;
sommet→ cle=nouvelle_valeur ;
}
void effacer(void)
{
while(!vide())
depiler() ;
}
12
Remarque :
La réalisation de l’opération effacer est basée sur des opérations fondamentales (ici vide et
depiler). Il s’agit d’une opération dite élaborée obtenue par composition des opérations
fondamentales. Par contre, change_sommet manipule des éléments liés à la représentation
physique de la pile. Si la structure de données pile est vide alors (implication) nb_elements=0.
De même, si le nombre d’éléments de la pile est nul alors cette pile est vide.
unsigned vide(void)
{
return(sommet==NULL) ;
}
ou
unsigned vide(void)
{
return(nb==0) ;
}
unsigned vide(void)
{
return(nb_elements()==0);
}
Dans de nombreux cas, il s'agit d'un programme distinct du compilateur lui-même et appelé
par celui-ci au début de la traduction. Le langage utilisé pour les directives du préprocesseur
est indépendant de la syntaxe du langage C, de sorte que le préprocesseur C peut être utilisé
isolément pour traiter d'autres types de fichiers sources.
Ces directives commencent toutes par le symbole dièse ( #), suivi d'un nombre quelconque de
blancs (espace ou tabulation), suivi du nom de la directive en minuscule. Les directives
doivent être déclarées sur une ligne dédiée. Les noms standards de directives sont :
1
http://hebergement.u-psud.fr/hamadache/lecture1_compilation.pdf
13
La compilation conditionnelle
Les directives #if, #ifdef, #ifndef, #else, #elif and #endif peuvent être utilisées pour la
compilation conditionnelle.
La plupart des compilateurs pour Microsoft Windows définissent implicitement _WIN32. Cela
permet au code, y compris les commandes de préprocesseur, de compiler uniquement lorsque
l'on cible les systèmes d'exploitation Windows. Quelques compilateurs définissent WIN32 à la
place. Pour les compilateurs qui ne définissent pas implicitement la macro _WIN32, il est
possible de le spécifier sur la ligne de commande du compilateur, en utilisant -D_WIN32.
#ifdef __unix__ // __unix__ est souvent défini par les compilateurs pour
des systèmes Unix
# include <unistd.h>
#elif defined _WIN32 // _Win32 est souvent défini par les compilateurs pour
des systèmes Windows 32 ou 64 bit
# include <windows.h>
#endif
Cet exemple de code teste si la macro __unix__ est définie. Si c'est le cas, le fichier
<unistd.h> est inclus. Sinon, il teste si la macro _WIN32 est définie, auquel cas le fichier
<windows.h> est inclus.
Documents :
• https://www.fil.univ-lille1.fr/portail/archive19-20/~sedoglav/PDC2/Cours04-2x3.pdf
• https://cours-
examens.org/images/Etudes_superieures/Ingenieur_en_automatique/5eme_annee/informatiqu
e_industrielle/Analyse_et_programmation_II/APR2_4_Le_preprocesseur.pdf
14