393 votes

Déclarer des Variables à l'intérieur des Boucles, des bonnes pratiques ou d'une mauvaise pratique? (2 Parties)

Question #1: Est la déclaration d'une variable à l'intérieur d'une boucle d'une bonne pratique ou une mauvaise pratique?
J'ai lu les autres discussions à ce sujet si il y a ou non un problème de performances (la plupart n'en dit pas), et que vous devez toujours déclarer des variables en tant que proche de l'endroit où ils vont être utilisés. Ce que je me pose est de déterminer si ou non ce qui doit être évité, ou si il est effectivement préférable.

Exemple:

for(int counter = 0; counter <= 10; counter++)
{
   string someString = "testing";

   cout << testing;
}

Question #2: Faire la plupart des compilateurs rendre compte que la variable a déjà été déclaré, et il suffit de sauter la partie, ou faut-il vraiment créer une place pour lui dans la mémoire à chaque fois?

492voto

Cyan Points 3186

C'est super pratique.

En créant des variables à l'intérieur des boucles, vous vous assurez de leur champ d'application est limité à l'intérieur de la boucle. Il ne peut pas être référencé, ni appelé à l'extérieur de la boucle.

De cette façon :

  • si le nom de la variable est un peu "générique" (comme "je"), il n'y a pas de risque de le mélanger avec une autre variable de même nom quelque part dans votre code plus tard (peut également être atténués en utilisant -Wshadow avertissement instructions sur GCC)

  • Le compilateur sait que la portée des variables est limité à l'intérieur de la boucle, et donc émettra un message d'erreur approprié, si la variable est par erreur appelé ailleurs

  • Dernier mais non le moins, certains d'optimisation peut être effectuée de manière plus efficace par le compilateur (le plus important d'allocation de registres), puisqu'il sait que la variable ne peut pas être utilisé en dehors de la boucle. Par exemple, pas besoin de stocker le résultat pour une utilisation ultérieure.

En bref, vous êtes en droit de le faire.

[Edit pour la question #2] : La variable est allouée une fois, lorsque la fonction est appelée. En fait, à partir d'une perspective d'allocation, il est (presque) la même chose que de déclarer les variables en début de fonction. La seule différence est la portée : la variable ne peut pas être utilisé en dehors de la boucle.

Avec restreints et plus précisément le champ d'application viennent plus précise de l'optimisation, de sorte que dans la fin, il peut être possible que la variable n'est même pas allouer ou de ré-utiliser un logements inutilisés (bien, très rarement pour celui-ci). Mais il ne sera jamais plus lent que d'allouer au début de la fonction.

[Edit 2] En réponse à un commentaire sur l'allocation des ressources : La règle ci-dessus est vrai en C, mais peut-être pas pour certaines classes C++.

Pour les types standard et des structures, la taille de la variable est connue au moment de la compilation. Il n'y a pas une telle chose comme "construction" dans C, de sorte que l'espace pour la variable sera simplement alloué dans la pile (sans initialisation), lorsque la fonction est appelée. C'est pourquoi il y a un "zéro" coût lors de la déclaration de la variable à l'intérieur d'une boucle.

Toutefois, pour les classes C++, il y a ce constructeur chose qui, je le connais beaucoup moins. Je suppose que l'allocation est probablement pas la question, puisque le compilateur doit être assez intelligent pour réutiliser le même espace, mais l'initialisation est susceptible d'avoir lieu à chaque itération de boucle.

[Edit 3] L'outil open-source CppCheck (un outil d'analyse statique de code C/C++) fournit d'excellents conseils concernant optimale de la portée des variables.

37voto

justin Points 72871

Généralement, c'est une très bonne pratique de la garder très proche.

Dans certains cas, il y aura un compte de facteurs tels que la performance qui justifie tirant la variable de la boucle.

Dans votre exemple, le programme crée et détruit la chaîne à chaque fois. Certaines bibliothèques utilisent une petite optimisation de la chaîne d'authentification unique (SSO), de sorte que l'allocation dynamique pourrait être évitée dans certains cas.

Supposons que vous vouliez éviter ces redondant créations/allocations, vous l'écrire comme:

for (int counter = 0; counter <= 10; counter++) {
   // compiler can pull this out
   const char testing[] = "testing";
   cout << testing;
}

ou vous pouvez le retirer de la constante:

const std::string testing = "testing";
for (int counter = 0; counter <= 10; counter++) {
   cout << testing;
}

Faire la plupart des compilateurs rendre compte que la variable a déjà été déclaré, et il suffit de sauter la partie, ou faut-il vraiment créer une place pour lui dans la mémoire à chaque fois?

Il peut réutiliser l'espace de la variable consomme, et il peut extraire les invariants de boucle. Dans le cas de la const char array (ci-dessus) - que le tableau pourrait être arraché. Toutefois, le constructeur et le destructeur doit être exécutée à chaque itération dans le cas d'un objet (tel qu' std::string). Dans le cas de l' std::string, que "l'espace" comporte un pointeur qui contient l'allocation dynamique représentant les personnages. Donc ceci:

for (int counter = 0; counter <= 10; counter++) {
   string testing = "testing";
   cout << testing;
}

aurait besoin redondant copie dans chaque cas, et l'allocation dynamique et gratuit si la variable se trouve au-dessus du seuil pour l'authentification unique personnage (et de l'authentification unique est mis en œuvre par votre bibliothèque std).

Pour ce faire:

string testing;
for (int counter = 0; counter <= 10; counter++) {
   testing = "testing";
   cout << testing;
}

aurait encore besoin d'une copie physique du personnage à chaque itération, mais la forme peut résulter en une seule allocation dynamique parce que vous attribuez à la chaîne et de la mise en œuvre devrait voir il n'est pas nécessaire de redimensionner la chaîne de sauvegarde de l'allocation. Bien sûr, vous ne le ferais pas, dans cet exemple (parce que de multiples supérieure alternatives ont déjà été démontré), mais vous pouvez considérer lors de la ficelle ou du vecteur contenu varie.

Alors que voulez-vous faire avec toutes ces options (et plus)? Tenir très près par défaut -- jusqu'à ce que vous comprendre les coûts de bien et de savoir, quand il faut s'écarter.

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