Vous êtes sur la page 1sur 14

Année Universitaire : 2023-2024

Section : Cycle Préparatoire Intégré en Sciences de l'Informatique


Niveau : 1ère année
Faculté des Sciences de Sfax Groupe : P2I-12
Département Informatique et des Communications Matière : Algorithmes, Structures de Données et Complexité
Enseignant : Mohamed Ali HADJ TAIEB

Ch ap i tre - Stru ctu re d e d o nn ées : Pi l e

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

Les opérations définies sur la SD pile obéissent aux propriétés suivantes :


(P1) creer_pile crée une pile vide.
(P2) si on ajoute un élément (en utilisant empiler) à une pile alors la pile résultante est non
vide.
(P3) un empilement suivi immédiatement d’un dépilement laisse la pile initiale inchangée.
Les deux opérations empiler et depiler s’exécutent par rapport au sommet de la pile.
(P4) On récupère les éléments d’une pile dans l’ordre inverse ou on les a mis (empiler).
(P5) Un dépilement suivi immédiatement de l’empilement de l’élément dépilé laisse la pile
inchangée.

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).

3. Représentation physique d’une Structure de donnée

Pour pouvoir matérialiser (concrétiser, réaliser ou implémenter) une SD on distingue deux


types de représentation :
- Représentation chaînée : les éléments d’une SD sont placés à des endroits
quelconques (bien entendu la MC) mais chaînés (ou reliés). Idée : pointeur.
- Représentation contiguë : les éléments d’un SD sont rangés (placés) dans un
espace contiguë. Idée : tableau.
3.1. Représentation chaînée de la SD pile
Utilisation des pointeurs pour relier explicitement les éléments formant la SD. La
représentation chaînée comporte plusieurs nœuds. Chaque nœud regroupe les champs
suivants :
✓ Le champ « cle » permet de mémoriser un élément de la pile.
✓ Le champ « suivant » permet de pointer sur l’élément précédemment empilé.
✓ La variable « sommet » permet de pointer ou de repérer l’élément au sommet
de la pile.

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.

Avec sommet est un indice compris entre 0 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 ;
};

static struct element *sommet ; /* Pour restreindre la portée de cette variable à ce


fichier, il faut utiliser le mot réservé static on dit que la variable sommet n’est pas destinée à
l’exportation*/
void creer pile(void)
{
sommet=NULL ;
}
unsigned vide(void)
{
return(sommet==NULL) ;
}
int dernier(void)
{
assert(!vide());
return(sommet →cle) ;
}
void empiler(int info)
{
struct element*p;
p=(struct element*)malloc(sizeof(struct element));
assert(p!=null);
p→cle=info;
p→suivant=sommet;
/*mettre à jour sommet*/
sommet=p;
}

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 ;
} ;

/*opérations ou services exportés*/


struct element*creer_pile(void) ;
unsigned vide(struct element*) ;
int dernier(struct element*) ;
void empiler(int,struct element**) ;
void depiler(struct element**) ;

✓ Partie implémentation : pile.c


#include <stdio.h>
#include <alloc.h>
#include <assert.h>
#include "pile.h"
/*réalisation des opérations exportées par pile.h*/
struct element*creer_pile(void)
{
return NULL;
}
unsigned vide(struct element*p)
{
return(p==NULL);
}
int dernier(struct element*p)
{
assert(!vide(p)) ;
return(p→cle) ;
}
void empiler(int info, struct element**p)
{
struct element*q ;
assert(p!=NULL);
q=(struct element*) malloc(sizeof(struct element)) ;
assert(q!=NULL);
q →cle=info;
q →suivant=*p;
/*mise à jour*/
*p=q;
}
void depiler(struct element **p)
{
struct element *q;
assert(p!=NULL);
assert(!vide(*p));
q=*p ;
*p=(*p) → suivant ;
free(q) ;
}

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

Dans ce paragraphe, on va évaluer les possibilités fournies par C vis-à-vis de la


matérialisation d’une SD en prenant comme exemple la SD Pile.
5.1. Position ou emplacement de la représentation physique de la SD
Position de la représentation physique d’une SD

objet abstrait : partie implémentation TDA : partie interface


5.1.1. Utilisation SD Pile OA
Lors de la manipulation de l’utilisation d’une SD fournie sous forme d’un OA, on ne peut pas
manipuler les aspects liés à la représentation physique d’une telle SD.
#include "pile.h"


creer_pile();

5.1.2. Utilisation d’un type de données abstrait (cas SD Pile)


Lors de la manipulation de l’utilisation d’une SD fournie sous forme d’un TDA, on peut
manipuler les aspects liés à la représentation physique d’une telle SD.

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

5.3. Positions des assertions


Il y a des opérations sur la SD exigeant des pré-conditions. Par exemple, depiler exige que la
SD Pile soit non vide. Dans les deux variantes (OA et TDA) les assertions figurent dans la
partie implémentation car assert est considérée comme une macro-instruction.
interface
SD Pile implémentation
utilisation (clients)
Les assertions traduisent des pré-conditions concernent les clients de cette SD.
En principe, la place des pré-conditions est l’interface et non pas dans la partie
implémentation. Les pré-conditions sont considérées comme des obligations du client
(utilisation) vis-à-vis du fournisseur (ici la SD).
En conclusion, le langage C est parfois non adapté à la matérialisation des SD.

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*/

✓ On peut envisager plusieurs opérations dites secondaires (ou non fondamentales)


applicables sur la SD Pile. Parmi ces opérations, on cite :

nb_elements : fournit le nombre d’éléments d’une pile ➔ opération de consultation.

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

✓ Application : cadre objet abstrait

objet abstrait : pile.h et pile.c


modification apportée au pile.h :
/* on garde inchangé la déclaration des opérations fondamentales : creer_pile, vide…*/
unsigned nb_elements(void);
void effacer(void) ;
void change_sommet(int) ;
modification apportée au pile.c :
unsigned nb(void)
{
unsigned nb ;
nb=0;
while(!vide())
{
nb++;
depiler();
}
return nb;
}
static unsigned nb;/*nombre d’éléments dans la pile*/

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);
}

Explorer un peu plus...


Préprocesseur C

Le préprocesseur C1 assure une phase préliminaire de la traduction (compilation) des


programmes informatiques écrits dans les langages de programmation C et C++. Comme
préprocesseur, il permet principalement l'inclusion d'un segment de code source disponible
dans un autre fichier (fichiers d'en-tête ou header), la substitution de chaînes de caractères
(macro définition), ainsi que la compilation conditionnelle.

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 :

• define : définit un motif de substitution.


• undef : retire un motif de substitution.
• include : inclusion de fichier.
• ifdef, ifndef, if, else, elif, endif : compilation conditionnelle.
• pragma : extension du compilateur.
• error : émettre un message d'erreur personnalisé et stopper la compilation.

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.

#if VERBOSE >= 2


printf("Hello world!");
#endif

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.

Il est possible d'utiliser des opérateurs avec la directive #if, comme :

#if !(defined __LP64__ || defined __LLP64__) || defined _WIN32 && !defined


_WIN64
// compilation pour un système 32-bit
#else
// compilation pour un système 64-bit
#endif

La majorité des langages de programmation modernes n'utilisent pas ces fonctionnalités et


dépendent plutôt d'une utilisation des habituels opérateurs if…then…else…, laissant au
compilateur la tâche de supprimer le code inutile.

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

Vous aimerez peut-être aussi