188 votes

Stack, Static et Heap en C ++

J'ai cherché, mais je n'ai pas très bien compris ces trois concepts. Quand dois-je utiliser l'allocation dynamique (dans le tas) et quel est son réel avantage? Quels sont les problèmes de statique et de la pile? Pourrais-je écrire une application entière sans allocation des variables dans le tas?

J'ai entendu dire que les autres langues incorporer un "garbage collector" donc, vous n'avez pas à vous soucier de la mémoire. Quel est le garbage collector?

Que pourriez-vous faire de la manipulation de la mémoire par vous-même que vous ne pouvais pas le faire à l'aide de ce garbage collector?

Une fois quelqu'un m'a dit qu'avec cette déclaration:

int * asafe=new int;

J'ai un "pointeur vers un pointeur". Ça veut dire quoi? Il est différent de:

asafe=new int;

?

250voto

markets Points 3598

Une question similaire a été posée, mais elle ne m'a pas demandé de la statique.

Résumé de ce que statique, de tas et de la pile de la mémoire sont:

  • Une variable statique est fondamentalement une variable globale, même si vous ne pouvez pas accéder à l'échelle mondiale. Habituellement, il ya une adresse pour ce qui est de l'exécutable lui-même. Il n'y a qu'un seul exemplaire pour l'ensemble du programme. Peu importe combien de fois vous allez dans un appel de fonction (ou de classe) (et en combien de fils!) la variable fait référence au même emplacement mémoire.

  • Le tas est un bouquet de mémoire qui peut être utilisé de façon dynamique. Si vous souhaitez de 4 ko pour un objet, alors la dynamique de l'allocateur regardez à travers la liste d'espace libre dans le tas, choisissez un bloc de 4 ko, et le donner à vous. Généralement, la dynamique de l'allocateur de mémoire (malloc, de nouvelles, et. c.) commence à la fin de la mémoire et fonctionne à l'envers.

  • Expliquer comment une pile grandit et rétrécit est un peu en dehors de la portée de cette réponse, mais il suffit de dire que vous avez toujours ajouter et supprimer à partir de la fin seulement. Les piles commencent habituellement en haut et pousser vers le bas pour abaisser les adresses. Vous manquez de mémoire lors de la pile répond à la dynamique de l'allocateur quelque part dans le milieu (mais reportez-vous à la physique rapport à la mémoire virtuelle et de la fragmentation). Plusieurs threads vont nécessiter plusieurs piles (de manière générale, les réserves de la taille minimale de la pile).

Lorsque vous souhaitez utiliser:

  • Statique/globales sont utiles pour mémoire que vous savez que vous aurez toujours besoin et vous savez que vous ne voulez pas jamais désallouer. (En passant, intégré des milieux peut être considéré comme ayant seulement statique de la mémoire... la pile et le tas sont partie d'un espace d'adressage partagé par un troisième type de mémoire: le code du programme. Les programmes vont souvent faire de l'allocation dynamique de leur mémoire statique lorsqu'ils ont besoin de choses comme les listes chaînées. Mais peu importe, la statique de la mémoire elle-même (le tampon) n'est pas "affecté", mais plutôt d'autres objets sont alloués de la mémoire occupée par le tampon à cette fin. Vous pouvez le faire en non-incorporés en tant que bien, et console de jeux souvent échappent à la construit dans la mémoire dynamique des mécanismes en faveur de la contrôlant étroitement le processus d'attribution, en utilisant des tampons de tailles prédéfinies pour toutes les allocations.)

  • Pile les variables sont utiles quand vous savez que tant que la fonction est dans la portée (sur la pile quelque part), vous voulez les variables de rester. Les piles sont sympas pour les variables que vous avez besoin pour le code où ils sont situés, mais qui n'est pas nécessaire, en dehors de ce code. Ils sont vraiment très sympa pour lorsque vous accédez à une ressource, comme un fichier, et que vous voulez les ressources pour passer automatiquement à l'écart lorsque vous quittez le code.

  • Les allocations de tas (de mémoire allouée dynamiquement) est utile lorsque vous voulez être plus souple que le précédent. Souvent, une fonction est appelée à répondre à un événement (l'utilisateur clique sur "créer" de la boîte de bouton). La bonne réponse peut exiger l'allocation d'un nouvel objet (une nouvelle Boîte de dialogue de l'objet) qui restent longtemps après que la fonction est sorti, donc il ne peut pas être sur la pile. Mais vous ne savez pas le nombre de cases que vous voulez au début du programme, de sorte qu'il ne peut pas être statique.

La Collecte Des Ordures

J'en ai beaucoup entendu parler ces derniers temps sur la façon dont grand éboueurs sont, donc peut-être un peu d'une voix dissidente serait utile.

La Collecte des ordures est un merveilleux mécanisme pour quand la performance n'est pas un énorme problème. J'entends les tables sont de mieux en mieux et de plus en plus sophistiquées, mais le fait est, vous pouvez être contraint d'accepter une dégradation de la performance (en fonction des cas d'utilisation). Et si vous êtes paresseux, il pourrait ne pas fonctionner correctement. Dans le meilleur des cas, des éboueurs se rendre compte que votre mémoire s'en va quand il se rend compte qu'il n'y a pas plus de références (voir le comptage de référence). Mais, si vous avez un objet qui se réfère à lui-même (peut-être par référence à un autre objet qui se réfère à l'arrière), puis de comptage de référence seront les seuls à ne pas indiquer que la mémoire peut être effacée. Dans ce cas, le GC doit regarder l'ensemble de référence de la soupe et de déterminer s'il y a des îles qui ne sont visées par eux-mêmes. Désinvolte, je suppose que pour être un O(n^2), mais quel qu'il soit, il peut faire mal si vous êtes à tous les intéressés à la performance. (Edit: Martin B souligne qu'il est O(n) pour raisonnablement efficace des algorithmes. Qui est toujours en O(n) trop si vous êtes concerné par les performances et peut libérer à temps constant sans garbage collection).

Personnellement, quand j'entends des gens dire que le C++ n'a pas de collecte des ordures, mon esprit balises que comme une caractéristique de C++, mais je suis probablement en minorité. Probablement la chose la plus difficile pour les gens à apprendre la programmation en C et C++ sont des pointeurs et de la façon de gérer correctement leur dynamique de l'allocation mémoire. Quelques autres langues, comme le Python, serait horrible sans GC, donc je crois qu'au fond, ce que vous voulez de une langue. Si vous voulez des performances fiables, alors C++ sans la collecte des ordures est la seule chose de ce côté de Fortran que je peux penser. Si vous voulez la facilité d'utilisation et la formation des roues (pour vous sauver de s'écraser sans que vous ayez à apprendre "bonne" gestion de la mémoire), choisissez quelque chose avec un GC. Même si vous savez comment gérer la mémoire bien, il vous fera gagner du temps que vous pouvez passer de l'optimisation de code. Il n'y a vraiment pas beaucoup de performances en plus, mais si vous avez vraiment besoin d'offrir des performances fiables (et la possibilité de savoir exactement ce qui se passe, quand, sous les couvertures) puis je coller avec C++. Il ya une raison que chaque grand moteur de jeu que j'ai jamais entendu parler de est en C++ (si pas de C ou de l'assemblée). Python, et al sont très bien pour le script, mais pas le principal moteur de jeu.

58voto

Ce qui suit est bien sûr tout n'est pas assez précis. Prendre avec un grain de sel quand vous le lire :)

Ainsi, les trois choses que vous consultez sont automatiques, statique et dynamique de la durée de stockage, qui a quelque chose à voir avec combien de temps les objets de vivre et quand ils commencent leur vie. Vous utilisez automatique de la durée de stockage de courte durée et de petites données, ce qui est nécessaire seulement localement dans certains bloc:

if(some condition) {
    int a[3]; // array a has automatic storage duration
    fill_it(a);
    print_it(a);
}

La durée de vie se termine dès que nous quittez le bloc, et ça commence dès que l'objet est défini. Ils sont les plus simples de la durée de stockage, et sont plus rapides que dans la dynamique particulière de stockage durée.

Vous utilisez statique de la durée de stockage de variables libres, qui peut être accessible par un code tout temps, si leur champ d'application permet une telle utilisation (espace de noms de champ), et pour les variables locales qui ont besoin d'allonger leur durée de vie à travers la sortie de leur portée (portée locale), et pour les variables membres, qui doivent être partagées par tous les objets de la classe (classs champ d'application). Leur durée de vie dépend de la portée, ils sont. Ils peuvent avoir de l' espace de noms de la portée et de l' étendue locale et de la portée de classe. Ce qui est vrai à propos de deux d'entre eux, une fois que leur vie commence, la durée de vie se termine à la fin du programme. En voici deux exemples:

// static storage duration. in global namespace scope
string globalA; 
int main() {
    foo();
    foo();
}

void foo() {
    // static storage duration. in local scope
    static string localA;
    localA += "ab"
    cout << localA;
}

Le programme imprime abababcar localA n'est pas détruit lors de la sortie de son bloc. Vous pouvez dire que les objets qui ont une portée locale commencer à vie lorsque le contrôle atteint leur définition. Pour localA, ce qui se passe lorsque la fonction du corps est entré. Pour les objets dans l'espace de noms de la portée, la durée de vie commence au démarrage du programme. Le même est vrai pour les objets statiques de la classe de la portée:

class A {
    static string classScopeA;
};

string A::classScopeA;

A a, b; &a.classScopeA == &b.classScopeA == &A::classScopeA;

Comme vous le voyez, classScopeA n'est pas lié à des objets particuliers de sa classe, mais à la classe elle-même. L'adresse de tous les trois noms ci-dessus est le même, et tous les désigner le même objet. Il y a des règles spéciales concernant quand et comment les objets statiques sont initialisés, mais n'oublions pas d'inquiétude à ce sujet maintenant. Qu'entend-on par le terme statique de l'ordre d'initialisation fiasco.

La dernière durée de stockage est dynamique. Vous l'utilisez si vous voulez avoir des objets de vivre sur une autre île, et vous voulez mettre des pointeurs autour de cette référence. Vous avez également de les utiliser si vos objets sont gros, et si vous voulez créer des tableaux de taille connu au runtime. En raison de cette flexibilité, des objets ayant une dynamique de durée de stockage sont longue et complexe à gérer. Les objets de cette dynamique durée commencer à vie, lorsque approprié nouvel opérateur invocation qui se passe:

int main() {
    // the object that s points to has dynamic storage 
    // duration
    string *s = new string;
    // pass a pointer pointing to the object around. 
    // the object itself isn't touched
    foo(s);
    delete s;
}

void foo(string *s) {
    cout << s->size();
}

Sa durée de vie ne se termine que lorsque vous appelez supprimer pour eux. Si vous oubliez que, ces objets n'ont pas de fin de vie. Et les objets de la classe que de définir un utilisateur déclaré constructeur de ne pas avoir leurs destructeurs appelé. Des objets ayant une dynamique de durée de stockage nécessite la manutention manuelle de leur durée de vie et la mémoire associée à cette ressource. Les bibliothèques existent pour faciliter leur utilisation. Explicite de collecte des ordures pour des objets particuliers peuvent être établis à l'aide d'un pointeur intelligent:

int main() {
    shared_ptr<string> s(new string);
    foo(s);
}

void foo(shared_ptr<string> s) {
    cout << s->size();
}

Vous n'avez pas de soins sur l'appel de supprimer: La partagé ptr-t-il pour vous, si le dernier pointeur qui fait référence à l'objet est hors de portée. L'partagé ptr a lui-même automatique de la durée de stockage. Donc sa durée de vie est géré automatiquement, permettant de vérifier s'il doit supprimer l'a souligné objet dynamique dans son destructeur. Pour shared_ptr de référence, voir le boost de documents: http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm

45voto

peterchen Points 21792

Il a été dit minutieusement, tout comme "la réponse courte":

  • variable statique (la classe)
    durée de vie = programme d'exécution (1)
    visibilité = déterminée par des modificateurs d'accès (privé/protégé/public)

  • variable statique (portée mondiale)
    durée de vie = programme d'exécution (1)
    visibilité = l'unité de compilation, il est instancié dans (2)

  • tas variable
    durée de vie = défini par vous (nouvelle à supprimer)
    visibilité = défini par vous (ce que vous affectez le pointeur)

  • pile variable
    visibilité = de la déclaration jusqu'à ce que la portée est sorti
    durée de vie = de la déclaration jusqu'à déclarer champ d'application est quittée


(1) plus exactement: de l'initialisation jusqu'à deinitialization de l'unité de compilation (c'est à dire le C / C++ fichier). Ordre de l'initialisation des unités de compilation n'est pas défini par la norme.

(2) Attention: si vous instancier une variable statique dans un en-tête, chaque unité de compilation a sa propre copie.

5voto

Chris Smith Points 7465

Je suis sûr que l'un des pédants, viendra avec une meilleure réponse dans peu de temps, mais la principale différence est la vitesse et de la taille.

Pile

Beaucoup plus rapide à allouer. Il est fait en O(1), puisqu'il est alloué lors de la configuration de la trame de pile de sorte qu'il est pratiquement libre. L'inconvénient est que si vous manquez d'espace de pile vous sont désossées. Vous pouvez ajuster la taille de la pile, mais SI vous avez ~2 mo pour jouer avec. Aussi, dès que vous quittez la fonction de tout sur la pile est effacée. De sorte qu'il peut être problématique pour le consulter plus tard. (Pointeurs de pile d'objets alloués conduit à des bugs.)

Tas

Bien plus de temps à allouer. Mais vous avez GO pour jouer avec, et le point de.

Garbage Collector

Le garbage collector est un code qui fonctionne dans le fond et libère la mémoire. Lorsque vous allouer de la mémoire sur le tas, il est très facile d'oublier de le libérer, ce qui est connu comme une fuite de mémoire. Au fil du temps, la mémoire de votre application consomme grandit et se développe jusqu'à ce qu'il se bloque. Avoir un garbage collector régulièrement libérer de la mémoire, vous n'avez plus besoin d'aide à éliminer cette classe de bugs. Bien sûr, cela vient à un prix, que le garbage collector ralentit les choses.

3voto

ChrisW Points 37322

Quels sont les problèmes de statique et de la pile?

Le problème avec "statique" de l'allocation, c'est que la répartition est faite au moment de la compilation: vous ne pouvez pas l'utiliser pour allouer un certain nombre variable de données, dont le nombre n'est pas connu jusqu'à ce moment de l'exécution.

Le problème avec l'allocation sur la "pile" est que la répartition est détruit dès que la sous-routine qui ne l'allocation de retour.

Je pourrais écrire une application entière sans allouer les variables dans le tas?

Peut-être mais pas à un non-trivial, normal, grosse application (mais la soi-disant "embedded" programmes peuvent être écrits sans le tas, à l'aide d'un sous-ensemble de C++).

Ce garbage collector ne ?

Il continue de regarder vos données ("marque et de balayage") pour détecter si votre demande n'est plus le référencement. Ceci est pratique pour l'application, car l'application n'a pas besoin de libérer les données ... mais le garbage collector peut être gourmand en ressources.

Les éboueurs ne sont pas une habitude caractéristique de la programmation en C++.

Que pourriez-vous faire de la manipulation de la mémoire par vous-même que vous ne pouvais pas le faire à l'aide de ce garbage collector?

Apprendre le C++ mécanismes déterministes libération mémoire:

  • "statiques": ne jamais libéré
  • une "pile": dès que la variable "est hors de portée"
  • 'tas': lorsque le pointeur est supprimé (explicitement supprimées par l'application, ou implicitement supprimé dans certains-ou-autres sous-routine)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X