Une liste chaînée peut être utilisée lorsque vous souhaitez insérer et supprimer des éléments à bas prix et que peu importe que les éléments ne soient pas côte à côte en mémoire.
C'est très abstrait et j'aimerais une explication concrète de la raison pour laquelle une liste chaînée devrait être utilisée plutôt qu'un tableau. Je ne suis pas très expérimenté en programmation, donc je n'ai pas beaucoup d'expérience (le cas échéant) dans le monde réel.
Voici quelque chose à mi-chemin entre un exemple et une analogie. Vous avez des courses à faire, alors prenez un morceau de papier et écrivez:
Ensuite, vous vous souvenez que vous devez également acheter des timbres. En raison de la géographie de votre ville, vous devez le faire après la banque. Vous pouvez copier toute votre liste sur une nouvelle feuille de papier:
ou vous pourriez gribouiller sur celui que vous aviez:
Comme vous avez pensé à d'autres courses, vous pouvez les écrire au bas de la liste, mais avec des flèches vous rappelant dans quel ordre les faire. Ceci est une liste liée. C'est plus rapide et plus facile que de copier toute la liste à chaque fois que vous ajoutez quelque chose.
Ensuite, votre téléphone portable sonne pendant que vous êtes à la banque "hé, j'ai les timbres, ne décrochez plus". Vous rayez simplement STAMPS de la liste, vous n'en réécrivez pas un tout nouveau sans STAMPS.
Maintenant, vous pouvez réellement implémenter une liste de courses dans le code (peut-être une application qui met vos courses en ordre en fonction de votre géographie) et il y a une chance raisonnable que vous utilisiez réellement une liste liée pour cela dans le code. Vous souhaitez ajouter et supprimer de nombreux éléments, les commandes doivent être traitées, mais vous ne voulez pas recopier la liste entière après chaque insertion ou suppression.
La "pile d'appels" en langage C est implémentée en tant que liste liée dans les API binaires x86 (et la plupart des autres).
Autrement dit, l'appel de procédure en langage C suit une discipline premier entré, dernier sorti. Le résultat de l'exécution (éventuellement récursive) des appels de fonction est appelé "pile d'appels", ou parfois même "pile".
L'instruction CALL
x86 finit par implémenter une liste chaînée à l'aide de la "pile d'appels". Une instruction CALL
pousse le contenu du registre% EIP, l'adresse de l'instruction après le CALL
dans la mémoire de la pile . Le prologue appelé fuction pousse le contenu du registre% EBP, l'adresse la plus basse des variables locales dans la fonction appelante, dans la mémoire de la pile. Ensuite, le prologue de la fonction appelée définit% EBP sur la base de pile de la fonction actuelle.
Cela signifie que% EBP est un pointeur vers un emplacement mémoire qui contient l'adresse de la valeur% EBP de la fonction appelante. Ce n'est rien de plus qu'une liste chaînée, implémentée partiellement dans le matériel via CALL
.
Dans la mesure où cela est bon, c'est la façon dont les processeurs x86 implémentent les appels de fonction, en particulier les appels de fonction où la fonction a sa propre copie d'arguments et des variables locales à la fonction. Chaque appel de fonction pousse des informations sur la "pile d'appels" qui permettent au CPU de reprendre là où il s'était arrêté dans la fonction appelante, sans interférence de la fonction appelée ou de la fonction appelante.
Une liste liée peut être utilisée pour implémenter une file d'attente de messages.
Une file d'attente de messages est une structure dans laquelle nous stockons des informations sur les événements pour un traitement ultérieur. Par exemple, lorsque l'utilisateur appuie sur une touche ou déplace la souris, il s'agit d'un événement. Une application peut être occupée au moment où l'événement se produit, on ne peut donc pas s'attendre à ce qu'elle traite l'événement au moment précis où il se produit. Ainsi, l'événement est placé dans une file d'attente de messages (informations sur la touche enfoncée ou l'emplacement de déplacement de la souris) et lorsque l'application a du temps à perdre, elle vérifie sa file d'attente de messages, en récupère les événements et traite leur. (Cela se produit dans un laps de temps de quelques millisecondes, donc ce n'est pas visible.)
D'après le scénario d'utilisation que je viens de décrire, il devrait être évident que nous ne nous soucions jamais d'avoir un accès aléatoire aux événements stockés dans la file d'attente de messages; nous nous soucions seulement de pouvoir y stocker des messages et de les récupérer. Il est donc judicieux d'utiliser une liste chaînée, qui offre un temps d'insertion/suppression optimal.
(Veuillez ne pas insister pour souligner qu'une file d'attente de messages est susceptible, ou plus probable, ou presque aussi probable, d'être mise en œuvre à l'aide d'une liste de tableaux circulaire; c'est un détail technique et il a une limitation: vous ne pouvez stocker que un nombre limité de messages.)