J'ai été interrogé sur la structure de mémoire de pile et pile dans une interview. Le gars m'a demandé quel est l'avantage d'avoir la pile? Je n'étais pas sûr de ce qu'il voulait dire. Quelles sont les autres façons de configurer l'espace d'adressage pour exécuter un programme c?D'après une interview: quel est l'avantage de la pile en C?
Répondre
L'avantage de la pile est qu'elle permet une récursion directe et indirecte. Dans les langages sans pile (comme Fortran), les variables locales de chaque fonction sont globalement allouées, donc si vous appelez une fonction deux fois sans y retourner, vous cloberez votre adresse de retour et vous êtes en difficulté. De plus, l'allocation de mémoire de Fortran à chaque procédure n'est pas très efficace si vous n'en utilisez que quelques-uns à la fois. L'avantage de la pile sur un tas est, comme l'a souligné Murali, une allocation et une désallocation plus efficaces. Techniquement, la pile peut faire référence à la pile dynamique conceptuelle (appel) ou à sa section de mémoire, donc les langages peuvent avoir une pile d'appels allouée dans l'espace dynamique du tas, ce qui pourrait faciliter la mise en œuvre de coroutines, continuations et fermetures.
Je viens de faire une recherche rapide sur ma spécification C et le mot "pile" apparaît exactement 0 fois. Comme vous pouvez vous y attendre, l'existence d'une pile est un choix d'implémentation. Je n'ai jamais entendu parler d'une mise en œuvre qui n'a pas avoir une pile, faites attention à vous. L'utilisation d'une pile est la manière la plus simple d'utiliser & espace de réutilisation pour les variables avec une durée de stockage automatique, je suppose. Votre compilateur pourrait tout aussi bien allouer de l'espace global pour chaque variable automatique de votre programme s'il le voulait, mais cela augmenterait votre empreinte mémoire assez rapidement. Cela vous causerait probablement des problèmes si vous aviez une fonction récursive aussi.
Un compilateur optimisé pourrait théoriquement réutiliser la mémoire sur plusieurs fonctions puisqu'il connaît les graphes d'appel possibles, même si vous n'utilisez pas de pile. Le vrai problème serait la récursivité, comme vous l'avez mentionné. –
Bien sûr, le logiciel peut tout faire - même pour la récursivité, il pourrait ajouter un code de pré/postamble de fonction pour allouer l'espace pour les variables automatiques du tas, par exemple. Yikes. –
La question aurait pu se rapporter à quelques choses. Il a peut-être fait référence à la pile d'appels, à la pile "pile" (c'est-à-dire où les paramètres et les variables locales sont stockés), ou génériquement à la structure de données.
La pile d'appel est extrêmement utile car elle permet de suivre l'exécution d'un programme. Comparez cela avec le calcul initial, où tout était une instruction de saut ou une instruction goto pour les langages de niveau "supérieur". Vous deviez savoir d'où votre "routine" a été invoquée afin que vous puissiez lui rendre le contrôle, ainsi que vous mettre d'accord sur un moyen standard de passer des arguments entre eux. En revanche, vous pouvez maintenant faire apparaître la pile et renvoyer le contrôle de l'application à l'appelant précédent sans savoir qui est cet appelant.
Les avantages/bénéfices de l'utilisation de la pile pour les variables locales sont nombreux. Supposons que le compilateur détermine une tranche de mémoire spécifique pour l'utilisation d'une variable locale (comme c'est le cas en C pour une variable locale statique). Si vous appelez de nouveau récursivement votre fonction, vous n'auriez pas d'espace séparé dans la RAM pour les variables locales et vous écraseriez vos variables locales de l'appelant. La seule autre façon d'y parvenir serait d'allouer dynamiquement de la mémoire vive à chaque appel, avec une pénalité de performance significative.
Les piles sont en général un moyen utile de gérer des formes de données inédites. Contrairement à une file d'attente (FIFO), une pile signifie que vous pouvez gérer les données les plus récentes en premier tout en laissant les anciennes données en dernier.
J'ai vu quelques réponses utiles ici.
Mon twopennyworth:
- Une pile est une décision de mise en œuvre afin de ne pas partie de C.
- Les processeurs ont des instructions de pile intégrées, ce qui permet une utilisation efficace des piles.
- L'appel d'un sous-programme lance une trame de pile qui contient les paramètres d'appel et les variables locales du sous-programme. Ceux-ci peuvent maintenant être adressés en tant que petits décalages d'un registre Stack Pointer sur le CPU.
- De même, les appels imbriqués génèrent plus de pile sur la pile, ce qui permet une bonne gestion de la portée des variables et des paramètres.
- La récupération de place est simplifiée. Le simple retour de la routine/fonction place l'adresse de l'image précédente dans le Stack Pointer et la trame de la pile indésirable disparaît effectivement. Les tas sont parfaits pour le stockage de données dynamique, mais chaque variable doit être référencée par son propre pointeur et sa création et destruction (garbage collection) a plus de frais généraux.
Je pense que ce qu'il cherchait peut-être sont des compromis pour allouer de la mémoire sur la pile par rapport au tas, mais ce n'est qu'une supposition. –
Etes-vous sûr qu'il voulait dire ** la pile ** et pas ** une ** pile? –
Comme Frank Krueger l'a laissé entendre, parlez-vous des piles et des tas comme des structures de données générales ou de la mémoire pile et tas d'un programme? – MAK