Le sujet de Pile (informatique) est un sujet qui a suscité intérêt et débat dans la société d'aujourd'hui. Depuis ses origines, Pile (informatique) est un objet d'étude et de réflexion, générant des opinions contradictoires et des positions contradictoires. Dans cet article, nous visons à aborder de manière objective et exhaustive différents aspects liés à Pile (informatique), de son contexte historique à sa pertinence dans le contexte actuel. Différentes perspectives seront analysées, des données pertinentes seront présentées et nous chercherons à offrir une vision globale et complète de Pile (informatique), dans le but de contribuer au débat autour de ce sujet très pertinent.
En informatique, une pile (en anglais stack) est une structure de données fondée sur le principe « dernier arrivé, premier sorti » (en anglais LIFO pour last in, first out), ce qui veut dire qu'en général, le dernier élément ajouté à la pile est le premier à en sortir[1].
La plupart des microprocesseurs gèrent nativement une pile pour les appels de routine[2]. Elle correspond alors à une zone de la mémoire, et le processeur retient l'adresse du dernier élément.
Dans l'architecture x86 32 bits, le registre ESP sert à indiquer l'adresse du sommet d'une pile dans la mémoire vive[3]. Les opcodes push et pop permettent respectivement d'empiler et de dépiler des données. Les opcodes call et ret utilisent la pile pour appeler une fonction et la quitter par la suite en retournant à l'instruction suivant immédiatement l'appel[4].
En cas d'interruption, les registres EFLAGS, CS et EIP sont automatiquement empilés. Dans le cas d'un changement de niveau de priorité lors de l'interruption, les registres SS et ESP le sont aussi.
Une autre pile existe dans les CPU x86, celle de l'unité de calcul flottant (FPU). Plus précisément, cette unité utilise une pile limitée à 8 éléments, et dont le fonctionnement s’apparente à un barillet.
L’élément du barillet courant est nommé st(0), les éléments suivants st(N) avec N compris entre 1 et 7. Cette pile permet d'effectuer des calculs à la manière d'une calculatrice manuelle, en empilant les valeurs, puis en appliquant une opération sur les dernières valeurs empilées par exemple.
Certains processeurs n'utilisent pas de registre générique, mais uniquement la pile. Les instructions concernent alors ses premiers éléments : par exemple les calculatrices Hewlett-Packard pour l'implémentation de la notation polonaise inverse[5], les processeurs Focus, ou les machines Burroughs de la gamme B 5000[6]. Les instructions sont alors souvent plus courtes, car elles n'ont pas à référencer des registres[7]. Le bytecode du langage Java utilise aussi cette astuce.
Dans la plupart des langages de programmation compilés, la pile d'exécution est l'endroit où sont empilés tout ou partie des paramètres d'appel des routines. Par ailleurs, on y crée un espace pour des variables locales. La pile est ainsi formée de cadres de piles (en anglais stack frames) comprenant pour chaque routine, en cours d'appel imbriqué, ses paramètres, ses variables locales et son point de retour.
Quelques langages, comme PostScript d'Adobe[8] ou la commande dc d'Unix, implémentent un mécanisme de pile (avec la notation polonaise inverse) pour les calculs.
Dans tous les langages de programmation, la pile d'exécution contient une quantité limitée de mémoire, habituellement déterminée au début du programme. La taille de la pile d'exécution dépend de nombreux facteurs : la conception du compilateur, l’architecture du processeur, l’utilisation du traitement multithread et la quantité de mémoire vive disponible. Lorsque trop d’informations sont enregistrées dans la pile d’exécution, la pile déborde et écrase des zones de programme à l’extérieur de la pile : on dit alors qu’il y a dépassement de pile. Il en résulte généralement une interruption du programme[9].
Voici les primitives communément utilisées pour manipuler des piles[10] :
Il n'existe pas de normalisation pour les primitives de manipulation de pile : leurs noms sont donc indiqués de manière informelle. Seules les trois premières sont réellement indispensables, les autres pouvant s'en déduire.
Cette implémentation est celle utilisée dans les processeurs — elle est simple et la pile occupe peu de place. Une implémentation sous forme de liste chaînée est également possible pour des programmes.
'''Classe Pile'''
Attributs :
pile : tableau de Objet
sommet : entier ''{indice du dernier element entre}''
Methodes
Mprocédure '''PUSH'''(element) ''{entre un element (Objet)}''
Mfonction '''POP'''() retourne Objet ''{sort un Objet}''
''{non données ici}''
Mfonction '''FULL'''() retourne booleen ''{la pile est-elle pleine ? (retourne sommet >= MAX)}''
Mfonction '''EMPTY'''() retourne booleen ''{la pile est-elle vide ? (retourne sommet <= 0)}''
Mfonction '''SIZE'''() retourne entier ''{retourne le nombre d'elements (retourne sommet)}
Mprocedure '''INIT'''() ''{constructeur (met sommet à 0)}
'''Mprocédure''' ''PUSH'' (element)
''{ajouter un élément sur la pile}''
Paramètres :
(D) element : Objet
(D/R) cible : Pile
Début
Si cible.sommet <= MAX
Alors
cible.sommet <- cible.sommet + 1
cible.pile <- element
Sinon
afficher("Pile pleine")
Fsi
Fin
'''Mfonction''' ''POP'' () retourne objet
''{enlever un élément de la pile et le renvoyer}''
Paramètres :
(D/R) cible : Pile
Variables :
Element : objet
Début
Si cible.sommet > 0
Alors
Element <- cible.pile
cible.sommet <- cible.sommet - 1
Sinon
afficher("Pile vide")
Fsi
Retourner Element
Fin