8
votes

Lorsque vous déclarez des variables dans {} dans la portée, utilisera-t-il toujours une mémoire après?

Dans cet exemple, même si je n'utiliserai jamais les variables Wndclassexx, X, Y, CX, CY, ils utiliseront toujours la mémoire lorsque je suis dans la boucle de message:

wnd_register(hInst);
wnd_create(hInst);


4 commentaires

Bonne question +1 Bien que cela soit sans effort de tester comme NightCracker dit. Aurait pu passer moins de temps à tester vous-même que de taper cette question!


Pourquoi voudriez-vous déclarer des variables que vous n'utilisez pas


C'est vraiment une micro-optimisation inutile. Les threads sous Windows par défaut ont une pile de 1 Mo. Vous craignez de gaspiller moins d'un centième d'un cent de votre espace de pile disponible.


Je n'ai pas testé parce que je pensais peut-être que si le compilateur a vu que je ne l'utilisais pas après cette portée (même si je pouvais), cela l'optimiserait


9 Réponses :


0
votes

Oh Dieu non, quatre entiers dans la mémoire tandis que le programme est en cours d'exécution, quels déchets!

  1. Essayez-le, une simple boîte à une autre tentative de les imprimer devrait suffire (je pense).
  2. N'importe pas même.

3 commentaires

En fait, wndclassex a environ 10 membres: p incluant une chaîne


Ne peut même pas faire ça. Vous obtiendrez une erreur de compilation, 'x' est non déclarée


La chaîne de WNDClass est probablement une constante et ne sortirait pas de l'espace sur la pile pour les habitants.



3
votes

Probablement pas, mais c'est un détail de mise en œuvre. Ils auront été détruits cependant (les appels destructeurs auront été faits s'il y en a une tâche). Que ce soit et lorsque le système récupère la mémoire utilisée pour le stockage automatique n'est pas spécifié par la norme. La plupart le rendent à peu près autant immédiatement auquel afaik.


6 commentaires

Toute mémoire dynamique utilisée à l'intérieur d'objets de classe, telles que dans les chaînes, doit être libérée immédiatement par le destructeur de l'objet. Je me demande s'il y a des compilateurs qui conservent la mémoire de la pile jusqu'à la fin de la fonction à des fins de la vitesse, même s'il n'est généralement que 1 instruction de le libérer?


La plupart des compilateurs réutiliseront agressivement l'espace de pile. Il serait parfaitement légal pour MSG et WC d'avoir exactement la même adresse dans ses deux exemples.


@Michael, je peux voir comment cela est possible pour le deuxième cas, mais pour le premier, le compilateur ne peut pas appeler le destructeur pour wc jusqu'à la fin de la fonction afin qu'il ne puisse pas partager la mémoire avec n'importe quoi.


Wndclassex est une structure simple et n'a pas de destructeur. Même si cela avait un destructeur, le compilateur pourrait réutiliser la mémoire tant que ce destructeur ne touche pas cette mémoire.


De plus, je tiens à souligner que ce serait légal pour un compilateur de le faire, et non que tous les compilateurs optimisants effectuent définitivement l'optimisation que j'ai énoncée.


@Michael, j'ai oublié le boîtier de la POD - si le compilateur sait que l'objet n'a pas de destructeur et vous semble également envers la variable n'est pas utilisé avant la fin du bloc, il est certainement libre de la réutiliser. Votre point sur l'optimisation étant facultatif est bien pris, mais comme vous l'avez impliqué précédemment, c'est facile que la plupart des compilateurs en profiteraient. Si l'objet a un destructeur en ligne, le compilateur pourrait en faire une analyse pour s'assurer qu'il n'y a pas d'effets secondaires, mais c'est un peu plus sify.



6
votes

Le compilateur a beaucoup de marge de manœuvre pour gérer des locaux simples, comme vous l'avez dans vos exemples. Ils peuvent vivre sur la pile, ils ne peuvent exister que comme des valeurs immédiates dans le code de la machine, ou ils peuvent simplement vivre dans des registres. L'espace de pile est généralement attribué à l'entrée sur une fonction. Le compilateur soustrait une certaine valeur du pointeur de pile pour faire de la place pour tous les habitants. Au retour de la fonction, le pointeur de pile est restauré à sa valeur d'origine. Cela ne se fait généralement pas à la sortie de différents blocs de portée. La plupart des compilateurs tenteront de réutiliser agressivement l'espace de pile dès que les variables ne sont plus utilisées. Dans votre exemple, il serait parfaitement légal pour X et Msg d'avoir exactement la même adresse sur la pile, car leur utilisation est non chevauchée.

Ma réponse à Cette question passe plus en détail sur la manière dont les variables locales sont allouées sur la pile.

Dans vos exemples, les constantes, CX et CY, n'auront probablement pas de mémoire qui ne leur apportent aucune mémoire au moment de l'exécution et simplement des valeurs immédiates dans le code généré. X et Y vivront probablement dans des registres jusqu'à ce qu'ils doivent être poussés sur la pile pour l'appel à Creewindow. WC et MSG seront presque certainement sur la pile.

Vous ne devriez pas vous inquiéter des micro-optimisations à ce niveau - laissez le compilateur allouer de l'espace pour les variables locales car elle voit en forme. Vous avez une pile de 1 Mo par défaut, la quantité de données consommée par ces variables ne serait même pas enregistrée en tant que bruit. Passez votre temps à vous préoccuper des problèmes plus intéressants à la place.


2 commentaires

+1, bien que réellement le compilateur ne puisse probablement pas se chevaucher chez WC et X, car WC a son adresse prise et transmis à une fonction, il peut donc être accessible dans un appel ultérieur alors que c'est toujours en direct, tel que Creewindow. Je dis probablement comme le compilateur pouvait éventuellement faire une analyse de la fonction croisée, mais la plupart ne le font pas


Oui, vous avez raison, prenant l'adresse empêche le compilateur de réutiliser l'espace. Le compilateur ne peut pas effectuer d'analyse de fonction de fonctionnement ici depuis que Regisclassexsex et Creewindow vivent dans un autre module.



0
votes

Vos variables déclarées à l'intérieur de la {} deviendront hors de portée et être perdues. En fait, vous obtiendrez une erreur de compilation si vous essayez de les utiliser en dehors du bloc: «X» est non déclaré. Cependant, ceci est bâclé. Faites simplement une fonction pour ce code, comme vous l'avez dit dans votre édition. Garder votre principal () que peu de lignes que possible est une bonne pratique de programmation.


0 commentaires

1
votes

Eh bien, je ne suis pas sûr d'eux à l'aide de la mémoire ou de ce que la norme indique à ce sujet.

Ce que je sais savoir, c'est qu'à la fin d'un bloc de mémoire {}, le destructeur sera appelé et que les variables seront inaccessibles. . Cela pourrait signifier que, bien que ce n'est pas libéré, il peut au moins être réutilisé. P>

Exemple: P>

struct Foo {
    Foo(void) { std::cout << "Hi!"; }
    ~Foo(void) { std::cout << "Bye!"; }
};

int main(int argc, char * argv[])
{
    {
        Foo bar; // <- Prints Hi!
    } // <- Prints Bye!

    // Memory used by bar is now available.
}


4 commentaires

"Cela pourrait signifier que, bien que ce n'est pas libéré, il peut au moins être réutilisé." - Vous n'avez aucun moyen de contrôler l'endroit où aller vos variables automatiques. Il n'y aurait pas de moyen de "réutiliser" la mémoire non donnée à l'arrière.


Notez que puisque vous avez un constructeur explicite qui exécute le code, le compilateur doit l'exécuter à la sortie du bloc. Cela ne dit rien de l'espace de pile sous-jacent.


@NOAH, apparaissant une variable hors de la pile et poussant un autre dos est essentiellement un nop. Pourquoi un compilateur ne ferait-il pas simplement de réutiliser la mémoire et de construire un nouvel objet sur place? vous pourrait ne pas contrôler où aller vos variables automatiques, mais le compilateur fait certainement.


@Mark - encore une fois, probablement. Une implémentation est libre de faire autrement. En fait, il n'a même pas besoin d'utiliser une pile. Les choses que nous savons arriver en C ++ sont que les destructeurs sont appelés et les noms devenant indisponibles. Le reste est à la hauteur de la mise en œuvre, qui va bien sûr faire quelque chose de raisonnable pour que ce soit utile, mais quel type d'astuces les tirettes de mise en œuvre qui peuvent violer une réponse de bon sens générique que nous pourrions être tentées de donner de telles réponses invisibles et éventuellement par erreur.



0
votes

Ils ne le feront pas. Ils ne vivront que jusqu'à la fin de leur bloc de joint.


0 commentaires

1
votes

Un conseil magique: Faites confiance à votre compilateur. strong> IT optimise. C'est intelligent. Il optimise mieux que la plupart d'entre nous.

Si vous n'êtes pas sûr, utilisez un profileur ou examinez la sortie de l'assembleur du compilateur après des optimisations. Mais rappelez-vous - des optimisations triviales sont quelque chose que vous devriez pas em> faire dans votre code, car il est inutile et blesse la lisibilité de votre code. P>

Certaines variables (en particulier les constantes) n'utiliseront pas N'importe quel em> sur la pile car ils seront soit mappés sur des registres de la CPU ou incorporés directement dans une instruction d'assembleur. P>

Cela implique que les codes: P>

int a = 123;
int b = 56;
int c = 400;
int d = b+c;
int e = d*198;
e *= value;
e += a;
func(e);


0 commentaires

0
votes

Si vous les mettez dans une périmètre imbriquée dans la fonction (votre première option), lorsque le contrôle atteint la fin de la portée, les variables deviennent inaccessibles (une erreur de compilation si vous les utilisez directement ou le comportement indéfini de l'exécution de Vous économisez un pointeur sur l'un d'entre eux), leurs destructeurs sont exécutés (s'il y a des destructeurs), et la mise en œuvre peut réutiliser leur espace de stockage dans le cadre de la pile. Mais les normes ne ne nécessitent pas pour réutiliser l'espace.

Si vous divisez votre fonction en deux (votre deuxième option) ... En termes de coiffure standard, il n'y a pas de différence! Lorsque la fonction revient, les variables deviennent inaccessibles, leurs destructeurs sont exécutés et la mise en œuvre peut réutiliser leur espace de stockage, mais il n'est pas tenu de. Et là, ont été ont été des implémentations sérieuses - bien que non de C / C ++ - qui ne recyclent pas immédiatement cette mémoire: voir le plus célibataire le papier " cheney sur le MTA "

Toutefois, toutes les implémentations de C / C ++ que je suis actuellement au courant de do recycler la mémoire allouée pour les variables locales de la fonction lorsqu'une fonction retourne. Le recyclage de la mémoire pour les variables de portée locale imbriquées est beaucoup moins certaine. En tout état de cause, comme plusieurs autres personnes ont mentionné, il ne vaut pas la peine de s'inquiéter de quelques dizaines d'octets d'espaces de pile dans ce contexte.

Personnellement, je briserais votre code en deux fonctions simplement parce que chaque fonction ne fonctionne qu'une tâche. C'est généralement mieux pour la maintenance à long terme.


0 commentaires

0
votes

Généralement Oui, si la variable réside sur la pile du tout, l'espace pour elle sera pris sur la pile pendant toute la durée de la fonction enfermante. Les compilateurs calculent généralement la quantité maximale d'espace que les variables de la fonction pouvaient occuper, puis rendre la fonction allouer tout cela à la fois lorsque la fonction est entrée pour la première fois. Les constructeurs et les destructeurs seront toujours appelés à l'entrée et à la sortie des champs internes. L'espace pour les variables dans une portée peut être réutilisé pour représenter des variables d'une portée distincte.


0 commentaires