Je programme depuis un moment mais c'est surtout Java et C #. Je n'ai jamais eu à gérer la mémoire par moi-même. J'ai récemment commencé à programmer en C ++ et je ne sais pas trop quand je dois stocker des choses sur la pile et quand les stocker sur le tas.
Je crois comprendre que les variables auxquelles on accède très fréquemment devraient être stockées sur la pile et les objets, les variables rarement utilisées et les grandes structures de données devraient toutes être stockées sur le tas. Est-ce correct ou suis-je incorrect?
Réponses:
Non, la différence entre pile et tas n'est pas la performance. C'est la durée de vie: toute variable locale à l'intérieur d'une fonction (tout ce que vous n'avez pas malloc () ou new) vit sur la pile. Il disparaît lorsque vous revenez de la fonction. Si vous voulez que quelque chose dure plus longtemps que la fonction qui l'a déclaré, vous devez l'allouer sur le tas.
Pour une meilleure compréhension de ce qu'est la pile, allez-y de l'autre côté - plutôt que d'essayer de comprendre ce que fait la pile en termes de langage de haut niveau, recherchez "call stack" et "call convention" et voyez ce que la machine le fait vraiment lorsque vous appelez une fonction. La mémoire de l'ordinateur n'est qu'une série d'adresses; "tas" et "pile" sont des inventions du compilateur.
la source
Je dirais:
Stockez-le sur la pile, si vous POUVEZ.
Stockez-le sur le tas, si vous en avez besoin.
Par conséquent, préférez la pile au tas. Certaines raisons possibles pour lesquelles vous ne pouvez pas stocker quelque chose sur la pile sont:
Il est possible, avec des compilateurs sensés, d'allouer des objets de taille non fixe sur le tas (généralement des tableaux dont la taille n'est pas connue au moment de la compilation).
la source
C'est plus subtil que ne le suggèrent les autres réponses. Il n'y a pas de division absolue entre les données sur la pile et les données sur le tas en fonction de la façon dont vous les déclarez. Par exemple:
Dans le corps d'une fonction, cela déclare un
vector
(tableau dynamique) de dix entiers sur la pile. Mais le stockage géré par levector
n'est pas sur la pile.Ah, mais (les autres réponses suggèrent) la durée de vie de ce stockage est limitée par la durée de vie de
vector
lui - même, qui ici est basée sur la pile, donc cela ne fait aucune différence comment il est implémenté - nous ne pouvons le traiter que comme un objet basé sur la pile avec une sémantique de valeur.Non. Supposons que la fonction soit:
Ainsi, tout ce qui a une
swap
fonction (et tout type de valeur complexe devrait en avoir une) peut servir de sorte de référence rebindable à certaines données de tas, sous un système qui garantit un seul propriétaire de ces données.Par conséquent, l'approche C ++ moderne consiste à ne jamais stocker l'adresse des données de tas dans des variables de pointeur local nues. Toutes les allocations de tas doivent être cachées à l'intérieur des classes.
Si vous faites cela, vous pouvez penser à toutes les variables de votre programme comme s'il s'agissait de types de valeurs simples, et oublier complètement le tas (sauf lors de l'écriture d'une nouvelle classe wrapper de type valeur pour certaines données de tas, ce qui devrait être inhabituel) .
Vous devez simplement conserver un peu de connaissances spéciales pour vous aider à optimiser: si possible, au lieu d'assigner une variable à une autre comme ceci:
échangez-les comme ceci:
parce que c'est beaucoup plus rapide et ne lance pas d'exceptions. La seule exigence est que vous n'ayez pas besoin
b
de continuer à conserver la même valeur (elle obtiendraa
la valeur de à la place, qui sera suppriméea = b
).L'inconvénient est que cette approche vous oblige à renvoyer les valeurs des fonctions via les paramètres de sortie au lieu de la valeur de retour réelle. Mais ils corrigent cela en C ++ 0x avec des références rvalue .
Dans les situations les plus compliquées de toutes, vous prendriez cette idée à l'extrême général et utiliseriez une classe de pointeur intelligent telle que celle
shared_ptr
qui est déjà dans tr1. (Bien que je soutienne que si vous semblez en avoir besoin, vous avez peut-être quitté le point idéal de l'applicabilité du C ++ standard.)la source
Vous stockeriez également un élément sur le tas s'il doit être utilisé en dehors de la portée de la fonction dans laquelle il est créé. Un idiome utilisé avec les objets de la pile est appelé RAII - cela implique l'utilisation de l'objet basé sur la pile comme enveloppe pour une ressource, lorsque l'objet est détruit, la ressource serait nettoyée. Les objets basés sur la pile sont plus faciles à suivre lorsque vous pouvez lever des exceptions - vous n'avez pas besoin de vous soucier de la suppression d'un objet basé sur le tas dans un gestionnaire d'exceptions. C'est pourquoi les pointeurs bruts ne sont normalement pas utilisés dans le C ++ moderne, vous utiliseriez un pointeur intelligent qui peut être un wrapper basé sur une pile pour un pointeur brut vers un objet basé sur un tas.
la source
Pour ajouter aux autres réponses, il peut aussi s'agir de performances, au moins un peu. Non pas que vous devriez vous en soucier à moins que cela ne vous concerne, mais:
L'allocation dans le tas nécessite de trouver un suivi d'un bloc de mémoire, ce qui n'est pas une opération à temps constant (et prend quelques cycles et une surcharge). Cela peut ralentir à mesure que la mémoire se fragmente et / ou que vous vous approchez de l'utilisation de 100% de votre espace d'adressage. D'un autre côté, les allocations de pile sont des opérations à temps constant, essentiellement «gratuites».
Une autre chose à considérer (encore une fois, vraiment importante si cela devient un problème) est que généralement la taille de la pile est fixe et peut être beaucoup plus basse que la taille du tas. Donc, si vous allouez de gros objets ou de nombreux petits objets, vous souhaiterez probablement utiliser le tas; si vous manquez d'espace de pile, le runtime lèvera l'exception titulaire du site. Ce n'est généralement pas un gros problème, mais une autre chose à considérer.
la source
Stack est plus efficace et plus facile à gérer les données étendues.
Mais le tas doit être utilisé pour tout ce qui dépasse quelques Ko (c'est facile en C ++, il suffit de créer un
boost::scoped_ptr
sur la pile pour contenir un pointeur vers la mémoire allouée).Considérez un algorithme récursif qui ne cesse de s'appeler. Il est très difficile de limiter et / ou de deviner l'utilisation totale de la pile! Alors que sur le tas, l'allocateur (
malloc()
ounew
) peut indiquer un manque de mémoire en retournantNULL
ou enthrow
ing.Source : noyau Linux dont la pile ne dépasse pas 8 Ko!
la source
std::unique_ptr
, ce qui devrait être préféré à toute bibliothèque externe comme Boost (bien que cela alimente le standard au fil du temps).Pour être complet, vous pouvez lire l'article de Miro Samek sur les problèmes d'utilisation du tas dans le contexte des logiciels embarqués .
Un tas de problèmes
la source
Le choix d'allouer sur le tas ou sur la pile est fait pour vous, en fonction de la façon dont votre variable est allouée. Si vous allouez quelque chose de manière dynamique, en utilisant un "nouvel" appel, vous allouez à partir du tas. Si vous allouez quelque chose en tant que variable globale ou en tant que paramètre dans une fonction, il est alloué sur la pile.
la source
À mon avis, il y a deux facteurs décisifs
Je préférerais utiliser stack dans la plupart des cas, mais si vous avez besoin d'accéder à une variable en dehors de la portée, vous pouvez utiliser le tas.
Pour améliorer les performances tout en utilisant des tas, vous pouvez également utiliser la fonctionnalité pour créer un bloc de tas et cela peut aider à gagner en performances plutôt que d'allouer chaque variable dans un emplacement mémoire différent.
la source
cela a probablement été très bien répondu. Je voudrais vous signaler la série d'articles ci-dessous pour avoir une compréhension plus approfondie des détails de bas niveau. Alex Darby a une série d'articles dans lesquels il vous guide avec un débogueur. Voici la troisième partie sur la pile. http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/
la source